xref: /minix3/minix/lib/libc/sys/sendto.c (revision 7ecc6a9247125543940025b38b2e27c7ab19b121)
1 #include <sys/cdefs.h>
2 #include "namespace.h"
3 #include <lib.h>
4 
5 #include <assert.h>
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <sys/ioctl.h>
12 #include <sys/socket.h>
13 #include <netinet/in.h>
14 
15 #include <net/gen/in.h>
16 #include <net/gen/ip_hdr.h>
17 #include <net/gen/ip_io.h>
18 #include <net/gen/tcp.h>
19 #include <net/gen/tcp_io.h>
20 #include <net/gen/udp.h>
21 #include <net/gen/udp_hdr.h>
22 #include <net/gen/udp_io.h>
23 
24 #define DEBUG 0
25 
26 static ssize_t _tcp_sendto(int sock, const void *message, size_t length,
27 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
28 static ssize_t _udp_sendto(int sock, const void *message, size_t length,
29 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len,
30 	nwio_udpopt_t *udpoptp);
31 static ssize_t _uds_sendto_conn(int sock, const void *message, size_t length,
32 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
33 static ssize_t _uds_sendto_dgram(int sock, const void *message, size_t length,
34 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
35 
36 /*
37  * Send a message on a socket.
38  */
39 static ssize_t
40 __sendto(int fd, const void * buffer, size_t length, int flags,
41 	const struct sockaddr * dest_addr, socklen_t dest_len)
42 {
43 	message m;
44 
45 	memset(&m, 0, sizeof(m));
46 	m.m_lc_vfs_sendrecv.fd = fd;
47 	m.m_lc_vfs_sendrecv.buf = (vir_bytes)buffer;
48 	m.m_lc_vfs_sendrecv.len = length;
49 	m.m_lc_vfs_sendrecv.flags = flags;
50 	m.m_lc_vfs_sendrecv.addr = (vir_bytes)dest_addr;
51 	m.m_lc_vfs_sendrecv.addr_len = dest_len;
52 
53 	return _syscall(VFS_PROC_NR, VFS_SENDTO, &m);
54 }
55 
56 ssize_t sendto(int sock, const void *message, size_t length, int flags,
57 	const struct sockaddr *dest_addr, socklen_t dest_len)
58 {
59 	int r;
60 	nwio_tcpopt_t tcpopt;
61 	nwio_udpopt_t udpopt;
62 	nwio_ipopt_t ipopt;
63 	int uds_sotype = -1;
64 
65 	r = __sendto(sock, message, length, flags, dest_addr, dest_len);
66 	if (r != -1 || (errno != ENOTSOCK && errno != ENOSYS))
67 		return r;
68 
69 	r= ioctl(sock, NWIOGTCPOPT, &tcpopt);
70 	if (r != -1 || errno != ENOTTY)
71 	{
72 		if (r == -1)
73 			return r;
74 		return _tcp_sendto(sock, message, length, flags,
75 			dest_addr, dest_len);
76 	}
77 
78 	r= ioctl(sock, NWIOGUDPOPT, &udpopt);
79 	if (r != -1 || errno != ENOTTY)
80 	{
81 		if (r == -1)
82 			return r;
83 		return _udp_sendto(sock, message, length, flags,
84 			dest_addr, dest_len, &udpopt);
85 	}
86 
87 	r= ioctl(sock, NWIOGUDSSOTYPE, &uds_sotype);
88 	if (r != -1 || errno != ENOTTY)
89 	{
90 		if (r == -1) {
91 			return r;
92 		}
93 
94 		if (uds_sotype == SOCK_DGRAM) {
95 
96 			return _uds_sendto_dgram(sock, message,
97 				length, flags,dest_addr, dest_len);
98 		} else {
99 
100 			return _uds_sendto_conn(sock, message,
101 				length, flags, dest_addr, dest_len);
102 		}
103 	}
104 
105 	r= ioctl(sock, NWIOGIPOPT, &ipopt);
106 	if (r != -1 || errno != ENOTTY)
107 	{
108 		ip_hdr_t *ip_hdr;
109 		const struct sockaddr_in *sinp;
110 		ssize_t retval;
111 		int saved_errno;
112 
113 		if (r == -1) {
114 			return r;
115 		}
116 
117 		sinp = (const struct sockaddr_in *)dest_addr;
118 		if (sinp->sin_family != AF_INET)
119 		{
120 			errno= EAFNOSUPPORT;
121 			return -1;
122 		}
123 
124 		/* raw */
125 		/* XXX this is horrible: we have to copy the entire buffer
126 		 * because we have to change one header field. Obviously we
127 		 * can't modify the user buffer directly..
128 		 */
129 		if ((ip_hdr = malloc(length)) == NULL)
130 			return -1; /* errno is ENOMEM */
131 		memcpy(ip_hdr, message, length);
132 		ip_hdr->ih_dst= sinp->sin_addr.s_addr;
133 
134 		retval = write(sock, ip_hdr, length);
135 
136 		saved_errno = errno;
137 		free(ip_hdr);
138 		errno = saved_errno;
139 		return retval;
140 	}
141 
142 	errno = ENOTSOCK;
143 	return -1;
144 }
145 
146 static ssize_t _tcp_sendto(int sock, const void *message, size_t length,
147 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
148 {
149 
150 	if (flags != 0) {
151 #if DEBUG
152 		fprintf(stderr, "sendto(tcp): flags not implemented\n");
153 #endif
154 		errno= ENOSYS;
155 		return -1;
156 	}
157 
158 	/* Silently ignore destination, if given. */
159 
160 	return write(sock, message, length);
161 }
162 
163 static ssize_t _udp_sendto(int sock, const void *message, size_t length,
164 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len,
165 	nwio_udpopt_t *udpoptp)
166 {
167 	int r, t_errno;
168 	size_t buflen;
169 	void *buf;
170 	struct sockaddr_in *sinp;
171 	udp_io_hdr_t *io_hdrp;
172 
173 	if (flags)
174 	{
175 #if DEBUG
176 		fprintf(stderr, "sendto(udp): flags not implemented\n");
177 #endif
178 		errno= ENOSYS;
179 		return -1;
180 	}
181 
182 	if (udpoptp->nwuo_flags & NWUO_RWDATONLY)
183 		return write(sock, message, length);
184 
185 	if ((udpoptp->nwuo_flags & NWUO_RP_ANY) ||
186 		(udpoptp->nwuo_flags & NWUO_RA_ANY))
187 	{
188 		if (!dest_addr)
189 		{
190 			errno= ENOTCONN;
191 			return -1;
192 		}
193 
194 		/* Check destination address */
195 		if (dest_len < sizeof(*sinp))
196 		{
197 			errno= EINVAL;
198 			return -1;
199 		}
200 		sinp= (struct sockaddr_in *) __UNCONST(dest_addr);
201 		if (sinp->sin_family != AF_INET)
202 		{
203 			errno= EAFNOSUPPORT;
204 			return -1;
205 		}
206 	}
207 
208 	buflen= sizeof(*io_hdrp) + length;
209 	if (buflen < length)
210 	{
211 		/* Overflow */
212 		errno= EMSGSIZE;
213 		return -1;
214 	}
215 	buf= malloc(buflen);
216 	if (buf == NULL)
217 		return -1;
218 
219 	io_hdrp= buf;
220 	io_hdrp->uih_src_addr= 0;	/* Unused */
221 	io_hdrp->uih_src_port= 0;	/* Will cause error if NWUO_LP_ANY */
222 	if (udpoptp->nwuo_flags & NWUO_RA_ANY)
223 		io_hdrp->uih_dst_addr= sinp->sin_addr.s_addr;
224 	else
225 		io_hdrp->uih_dst_addr= 0;
226 	if (udpoptp->nwuo_flags & NWUO_RP_ANY)
227 		io_hdrp->uih_dst_port= sinp->sin_port;
228 	else
229 		io_hdrp->uih_dst_port= 0;
230 	io_hdrp->uih_ip_opt_len= 0;
231 	io_hdrp->uih_data_len= 0;
232 
233 	memcpy(&io_hdrp[1], message, length);
234 	r= write(sock, buf, buflen);
235 	if (r == -1)
236 	{
237 		t_errno= errno;
238 		free(buf);
239 		errno= t_errno;
240 		return -1;
241 	}
242 	assert((size_t)r == buflen);
243 	free(buf);
244 	return length;
245 }
246 
247 static ssize_t _uds_sendto_conn(int sock, const void *message, size_t length,
248 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
249 {
250 
251 	/* for connection oriented unix domain sockets (SOCK_STREAM /
252 	 * SOCK_SEQPACKET)
253 	 */
254 
255 	if (flags != 0) {
256 #if DEBUG
257 		fprintf(stderr, "sendto(uds): flags not implemented\n");
258 #endif
259 		errno= ENOSYS;
260 		return -1;
261 	}
262 
263 	/* Silently ignore destination, if given. */
264 
265 	return write(sock, message, length);
266 }
267 
268 static ssize_t _uds_sendto_dgram(int sock, const void *message, size_t length,
269 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
270 {
271 	int r;
272 
273 	/* for connectionless unix domain sockets (SOCK_DGRAM) */
274 
275 	if (flags != 0) {
276 #if DEBUG
277 		fprintf(stderr, "sendto(uds): flags not implemented\n");
278 #endif
279 		errno= ENOSYS;
280 		return -1;
281 	}
282 
283 	if (dest_addr == NULL) {
284 		errno = EFAULT;
285 		return -1;
286 	}
287 
288 	/* set the target address */
289 	r= ioctl(sock, NWIOSUDSTADDR, (void *) __UNCONST(dest_addr));
290 	if (r == -1) {
291 		return r;
292 	}
293 
294 	/* do the send */
295 	return write(sock, message, length);
296 }
297