1/* SPDX-License-Identifier: BSD-3-Clause */
2/*
3 * Copyright (c) 1995 Danny Gasparovski.
4 */
5
6#include "slirp.h"
7
8static void sbappendsb(struct sbuf *sb, struct mbuf *m);
9
10void sbfree(struct sbuf *sb)
11{
12 free(sb->sb_data);
13}
14
15bool 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
37void 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 */
66void 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 */
125static 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 */
164void 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