xref: /minix/minix/lib/libc/sys/sendto.c (revision 045e0ed3)
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 	/* For old socket driver implementations, this flag is the default. */
70 	flags &= ~MSG_NOSIGNAL;
71 
72 	r= ioctl(sock, NWIOGTCPOPT, &tcpopt);
73 	if (r != -1 || errno != ENOTTY)
74 	{
75 		if (r == -1)
76 			return r;
77 		return _tcp_sendto(sock, message, length, flags,
78 			dest_addr, dest_len);
79 	}
80 
81 	r= ioctl(sock, NWIOGUDPOPT, &udpopt);
82 	if (r != -1 || errno != ENOTTY)
83 	{
84 		if (r == -1)
85 			return r;
86 		return _udp_sendto(sock, message, length, flags,
87 			dest_addr, dest_len, &udpopt);
88 	}
89 
90 	r= ioctl(sock, NWIOGUDSSOTYPE, &uds_sotype);
91 	if (r != -1 || errno != ENOTTY)
92 	{
93 		if (r == -1) {
94 			return r;
95 		}
96 
97 		if (uds_sotype == SOCK_DGRAM) {
98 
99 			return _uds_sendto_dgram(sock, message,
100 				length, flags,dest_addr, dest_len);
101 		} else {
102 
103 			return _uds_sendto_conn(sock, message,
104 				length, flags, dest_addr, dest_len);
105 		}
106 	}
107 
108 	r= ioctl(sock, NWIOGIPOPT, &ipopt);
109 	if (r != -1 || errno != ENOTTY)
110 	{
111 		ip_hdr_t *ip_hdr;
112 		const struct sockaddr_in *sinp;
113 		ssize_t retval;
114 		int saved_errno;
115 
116 		if (r == -1) {
117 			return r;
118 		}
119 
120 		sinp = (const struct sockaddr_in *)dest_addr;
121 		if (sinp->sin_family != AF_INET)
122 		{
123 			errno= EAFNOSUPPORT;
124 			return -1;
125 		}
126 
127 		/* raw */
128 		/* XXX this is horrible: we have to copy the entire buffer
129 		 * because we have to change one header field. Obviously we
130 		 * can't modify the user buffer directly..
131 		 */
132 		if ((ip_hdr = malloc(length)) == NULL)
133 			return -1; /* errno is ENOMEM */
134 		memcpy(ip_hdr, message, length);
135 		ip_hdr->ih_dst= sinp->sin_addr.s_addr;
136 
137 		retval = write(sock, ip_hdr, length);
138 
139 		saved_errno = errno;
140 		free(ip_hdr);
141 		errno = saved_errno;
142 		return retval;
143 	}
144 
145 	errno = ENOTSOCK;
146 	return -1;
147 }
148 
149 static ssize_t _tcp_sendto(int sock, const void *message, size_t length,
150 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
151 {
152 
153 	if (flags != 0) {
154 #if DEBUG
155 		fprintf(stderr, "sendto(tcp): flags not implemented\n");
156 #endif
157 		errno= ENOSYS;
158 		return -1;
159 	}
160 
161 	/* Silently ignore destination, if given. */
162 
163 	return write(sock, message, length);
164 }
165 
166 static ssize_t _udp_sendto(int sock, const void *message, size_t length,
167 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len,
168 	nwio_udpopt_t *udpoptp)
169 {
170 	int r, t_errno;
171 	size_t buflen;
172 	void *buf;
173 	struct sockaddr_in *sinp;
174 	udp_io_hdr_t *io_hdrp;
175 
176 	if (flags)
177 	{
178 #if DEBUG
179 		fprintf(stderr, "sendto(udp): flags not implemented\n");
180 #endif
181 		errno= ENOSYS;
182 		return -1;
183 	}
184 
185 	if (udpoptp->nwuo_flags & NWUO_RWDATONLY)
186 		return write(sock, message, length);
187 
188 	if ((udpoptp->nwuo_flags & NWUO_RP_ANY) ||
189 		(udpoptp->nwuo_flags & NWUO_RA_ANY))
190 	{
191 		if (!dest_addr)
192 		{
193 			errno= ENOTCONN;
194 			return -1;
195 		}
196 
197 		/* Check destination address */
198 		if (dest_len < sizeof(*sinp))
199 		{
200 			errno= EINVAL;
201 			return -1;
202 		}
203 		sinp= (struct sockaddr_in *) __UNCONST(dest_addr);
204 		if (sinp->sin_family != AF_INET)
205 		{
206 			errno= EAFNOSUPPORT;
207 			return -1;
208 		}
209 	}
210 
211 	buflen= sizeof(*io_hdrp) + length;
212 	if (buflen < length)
213 	{
214 		/* Overflow */
215 		errno= EMSGSIZE;
216 		return -1;
217 	}
218 	buf= malloc(buflen);
219 	if (buf == NULL)
220 		return -1;
221 
222 	io_hdrp= buf;
223 	io_hdrp->uih_src_addr= 0;	/* Unused */
224 	io_hdrp->uih_src_port= 0;	/* Will cause error if NWUO_LP_ANY */
225 	if (udpoptp->nwuo_flags & NWUO_RA_ANY)
226 		io_hdrp->uih_dst_addr= sinp->sin_addr.s_addr;
227 	else
228 		io_hdrp->uih_dst_addr= 0;
229 	if (udpoptp->nwuo_flags & NWUO_RP_ANY)
230 		io_hdrp->uih_dst_port= sinp->sin_port;
231 	else
232 		io_hdrp->uih_dst_port= 0;
233 	io_hdrp->uih_ip_opt_len= 0;
234 	io_hdrp->uih_data_len= 0;
235 
236 	memcpy(&io_hdrp[1], message, length);
237 	r= write(sock, buf, buflen);
238 	if (r == -1)
239 	{
240 		t_errno= errno;
241 		free(buf);
242 		errno= t_errno;
243 		return -1;
244 	}
245 	assert((size_t)r == buflen);
246 	free(buf);
247 	return length;
248 }
249 
250 static ssize_t _uds_sendto_conn(int sock, const void *message, size_t length,
251 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
252 {
253 
254 	/* for connection oriented unix domain sockets (SOCK_STREAM /
255 	 * SOCK_SEQPACKET)
256 	 */
257 
258 	if (flags != 0) {
259 #if DEBUG
260 		fprintf(stderr, "sendto(uds): flags not implemented\n");
261 #endif
262 		errno= ENOSYS;
263 		return -1;
264 	}
265 
266 	/* Silently ignore destination, if given. */
267 
268 	return write(sock, message, length);
269 }
270 
271 static ssize_t _uds_sendto_dgram(int sock, const void *message, size_t length,
272 	int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
273 {
274 	int r;
275 
276 	/* for connectionless unix domain sockets (SOCK_DGRAM) */
277 
278 	if (flags != 0) {
279 #if DEBUG
280 		fprintf(stderr, "sendto(uds): flags not implemented\n");
281 #endif
282 		errno= ENOSYS;
283 		return -1;
284 	}
285 
286 	if (dest_addr == NULL) {
287 		errno = EFAULT;
288 		return -1;
289 	}
290 
291 	/* set the target address */
292 	r= ioctl(sock, NWIOSUDSTADDR, (void *) __UNCONST(dest_addr));
293 	if (r == -1) {
294 		return r;
295 	}
296 
297 	/* do the send */
298 	return write(sock, message, length);
299 }
300