1 | /* SPDX-License-Identifier: BSD-3-Clause */ |
2 | /* |
3 | * Copyright (c) 1995 Danny Gasparovski. |
4 | */ |
5 | |
6 | #include "slirp.h" |
7 | |
8 | static void sbappendsb(struct sbuf *sb, struct mbuf *m); |
9 | |
10 | void sbfree(struct sbuf *sb) |
11 | { |
12 | free(sb->sb_data); |
13 | } |
14 | |
15 | bool sbdrop(struct sbuf *sb, int num) |
16 | { |
17 | int limit = sb->sb_datalen / 2; |
18 | |
19 | /* |
20 | * We can only drop how much we have |
21 | * This should never succeed |
22 | */ |
23 | if (num > sb->sb_cc) |
24 | num = sb->sb_cc; |
25 | sb->sb_cc -= num; |
26 | sb->sb_rptr += num; |
27 | if (sb->sb_rptr >= sb->sb_data + sb->sb_datalen) |
28 | sb->sb_rptr -= sb->sb_datalen; |
29 | |
30 | if (sb->sb_cc < limit && sb->sb_cc + num >= limit) { |
31 | return true; |
32 | } |
33 | |
34 | return false; |
35 | } |
36 | |
37 | void sbreserve(struct sbuf *sb, int size) |
38 | { |
39 | if (sb->sb_data) { |
40 | /* Already alloced, realloc if necessary */ |
41 | if (sb->sb_datalen != size) { |
42 | sb->sb_wptr = sb->sb_rptr = sb->sb_data = |
43 | (char *)realloc(sb->sb_data, size); |
44 | sb->sb_cc = 0; |
45 | if (sb->sb_wptr) |
46 | sb->sb_datalen = size; |
47 | else |
48 | sb->sb_datalen = 0; |
49 | } |
50 | } else { |
51 | sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size); |
52 | sb->sb_cc = 0; |
53 | if (sb->sb_wptr) |
54 | sb->sb_datalen = size; |
55 | else |
56 | sb->sb_datalen = 0; |
57 | } |
58 | } |
59 | |
60 | /* |
61 | * Try and write() to the socket, whatever doesn't get written |
62 | * append to the buffer... for a host with a fast net connection, |
63 | * this prevents an unnecessary copy of the data |
64 | * (the socket is non-blocking, so we won't hang) |
65 | */ |
66 | void sbappend(struct socket *so, struct mbuf *m) |
67 | { |
68 | int ret = 0; |
69 | |
70 | DEBUG_CALL("sbappend" ); |
71 | DEBUG_ARG("so = %p" , so); |
72 | DEBUG_ARG("m = %p" , m); |
73 | DEBUG_ARG("m->m_len = %d" , m->m_len); |
74 | |
75 | /* Shouldn't happen, but... e.g. foreign host closes connection */ |
76 | if (m->m_len <= 0) { |
77 | m_free(m); |
78 | return; |
79 | } |
80 | |
81 | /* |
82 | * If there is urgent data, call sosendoob |
83 | * if not all was sent, sowrite will take care of the rest |
84 | * (The rest of this function is just an optimisation) |
85 | */ |
86 | if (so->so_urgc) { |
87 | sbappendsb(&so->so_rcv, m); |
88 | m_free(m); |
89 | (void)sosendoob(so); |
90 | return; |
91 | } |
92 | |
93 | /* |
94 | * We only write if there's nothing in the buffer, |
95 | * ottherwise it'll arrive out of order, and hence corrupt |
96 | */ |
97 | if (!so->so_rcv.sb_cc) |
98 | ret = slirp_send(so, m->m_data, m->m_len, 0); |
99 | |
100 | if (ret <= 0) { |
101 | /* |
102 | * Nothing was written |
103 | * It's possible that the socket has closed, but |
104 | * we don't need to check because if it has closed, |
105 | * it will be detected in the normal way by soread() |
106 | */ |
107 | sbappendsb(&so->so_rcv, m); |
108 | } else if (ret != m->m_len) { |
109 | /* |
110 | * Something was written, but not everything.. |
111 | * sbappendsb the rest |
112 | */ |
113 | m->m_len -= ret; |
114 | m->m_data += ret; |
115 | sbappendsb(&so->so_rcv, m); |
116 | } /* else */ |
117 | /* Whatever happened, we free the mbuf */ |
118 | m_free(m); |
119 | } |
120 | |
121 | /* |
122 | * Copy the data from m into sb |
123 | * The caller is responsible to make sure there's enough room |
124 | */ |
125 | static void sbappendsb(struct sbuf *sb, struct mbuf *m) |
126 | { |
127 | int len, n, nn; |
128 | |
129 | len = m->m_len; |
130 | |
131 | if (sb->sb_wptr < sb->sb_rptr) { |
132 | n = sb->sb_rptr - sb->sb_wptr; |
133 | if (n > len) |
134 | n = len; |
135 | memcpy(sb->sb_wptr, m->m_data, n); |
136 | } else { |
137 | /* Do the right edge first */ |
138 | n = sb->sb_data + sb->sb_datalen - sb->sb_wptr; |
139 | if (n > len) |
140 | n = len; |
141 | memcpy(sb->sb_wptr, m->m_data, n); |
142 | len -= n; |
143 | if (len) { |
144 | /* Now the left edge */ |
145 | nn = sb->sb_rptr - sb->sb_data; |
146 | if (nn > len) |
147 | nn = len; |
148 | memcpy(sb->sb_data, m->m_data + n, nn); |
149 | n += nn; |
150 | } |
151 | } |
152 | |
153 | sb->sb_cc += n; |
154 | sb->sb_wptr += n; |
155 | if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen) |
156 | sb->sb_wptr -= sb->sb_datalen; |
157 | } |
158 | |
159 | /* |
160 | * Copy data from sbuf to a normal, straight buffer |
161 | * Don't update the sbuf rptr, this will be |
162 | * done in sbdrop when the data is acked |
163 | */ |
164 | void sbcopy(struct sbuf *sb, int off, int len, char *to) |
165 | { |
166 | char *from; |
167 | |
168 | from = sb->sb_rptr + off; |
169 | if (from >= sb->sb_data + sb->sb_datalen) |
170 | from -= sb->sb_datalen; |
171 | |
172 | if (from < sb->sb_wptr) { |
173 | if (len > sb->sb_cc) |
174 | len = sb->sb_cc; |
175 | memcpy(to, from, len); |
176 | } else { |
177 | /* re-use off */ |
178 | off = (sb->sb_data + sb->sb_datalen) - from; |
179 | if (off > len) |
180 | off = len; |
181 | memcpy(to, from, off); |
182 | len -= off; |
183 | if (len) |
184 | memcpy(to + off, sb->sb_data, len); |
185 | } |
186 | } |
187 | |