1/* -*- c-basic-offset: 8; -*-
2 *
3 * Copyright (c) 1993 W. Richard Stevens.  All rights reserved.
4 * Permission to use or modify this software and its documentation only for
5 * educational purposes and without fee is hereby granted, provided that
6 * the above copyright notice appear in all copies.  The author makes no
7 * representations about the suitability of this software for any purpose.
8 * It is provided "as is" without express or implied warranty.
9 */
10
11#include <stdio.h>
12#include <netdb.h>
13#include <sys/types.h>
14#include <netinet/in.h>
15#include <arpa/inet.h>
16#include "sock.h"
17
18/* Copy everything from stdin to "sockfd",
19 * and everything from "sockfd" to stdout. */
20
21void
22loop_udp(int sockfd)
23{
24	int maxfdp1, nread, ntowrite, stdineof, clilen, servlen, flags;
25	fd_set			rset;
26	struct sockaddr_in	cliaddr;		/* for UDP server */
27	struct sockaddr_in	servaddr;		/* for UDP client */
28
29#ifdef	HAVE_MSGHDR_MSG_CONTROL
30	struct iovec			iov[1];
31	struct msghdr			msg;
32
33#ifdef	IP_RECVDSTADDR		/* 4.3BSD Reno and later */
34	static struct cmsghdr  *cmptr = NULL;	/* malloc'ed */
35	struct in_addr			dstinaddr;		/* for UDP server */
36#define	CONTROLLEN	(sizeof(struct cmsghdr) + sizeof(struct in_addr))
37#endif	/* IP_RECVDSTADDR */
38
39#endif	/* MSG_TRUNC */
40
41	if (pauseinit)
42		sleep_us(pauseinit*1000);	/* intended for server */
43
44	flags = 0;
45	stdineof = 0;
46	FD_ZERO(&rset);
47	maxfdp1 = sockfd + 1;	/* check descriptors [0..sockfd] */
48
49	/* If UDP client issues connect(), recv() and write() are used.
50	   Server is harder since cannot issue connect().  We use recvfrom()
51	   or recvmsg(), depending on OS. */
52
53	for ( ; ; ) {
54		if (stdineof == 0)
55			FD_SET(STDIN_FILENO, &rset);
56		FD_SET(sockfd, &rset);
57
58		if (select(maxfdp1, &rset, NULL, NULL, NULL) < 0)
59			err_sys("select error");
60
61		if (FD_ISSET(STDIN_FILENO, &rset)) {
62			/* data to read on stdin */
63			if ( (nread = read(STDIN_FILENO, rbuf, readlen)) < 0)
64				err_sys("read error from stdin");
65			else if (nread == 0) {
66				/* EOF on stdin */
67				if (halfclose) {
68					if (shutdown(sockfd, SHUT_WR) < 0)
69						err_sys("shutdown() error");
70
71					FD_CLR(STDIN_FILENO, &rset);
72					stdineof = 1;	/* don't read stdin anymore */
73					continue;		/* back to select() */
74				}
75				break;		/* default: stdin EOF -> done */
76			}
77
78			if (crlf) {
79				ntowrite = crlf_add(wbuf, writelen, rbuf, nread);
80				if (connectudp) {
81					if (write(sockfd, wbuf, ntowrite) != ntowrite)
82						err_sys("write error");
83				} else {
84					if (sendto(sockfd, wbuf, ntowrite, 0,
85						   (struct sockaddr *) &servaddr, sizeof(servaddr))
86					    != ntowrite)
87						err_sys("sendto error");
88				}
89			} else {
90				if (connectudp) {
91					if (write(sockfd, rbuf, nread) != nread)
92						err_sys("write error");
93				} else {
94					if (sendto(sockfd, rbuf, nread, 0,
95						   (struct sockaddr *) &servaddr, sizeof(servaddr))
96					    != nread)
97						err_sys("sendto error");
98				}
99			}
100		}
101
102		if (FD_ISSET(sockfd, &rset)) {
103			/* data to read from socket */
104			if (server) {
105				clilen = sizeof(cliaddr);
106#ifndef	MSG_TRUNC	/* vanilla BSD sockets */
107				nread = recvfrom(sockfd, rbuf, readlen, 0,
108						 (struct sockaddr *) &cliaddr, &clilen);
109
110#else	/* 4.3BSD Reno and later; use recvmsg() to get at MSG_TRUNC flag */
111	      /* Also lets us get at control information (destination address) */
112
113				iov[0].iov_base = rbuf;
114				iov[0].iov_len  = readlen;
115				msg.msg_iov          = iov;
116				msg.msg_iovlen       = 1;
117				msg.msg_name         = (caddr_t) &cliaddr;
118				msg.msg_namelen      = clilen;
119
120#ifdef	IP_RECVDSTADDR
121				if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
122					err_sys("malloc error for control buffer");
123
124				msg.msg_control      = (caddr_t) cmptr;	/* for dest address */
125				msg.msg_controllen   = CONTROLLEN;
126#else
127				msg.msg_control      = (caddr_t) 0;	/* no ancillary data */
128				msg.msg_controllen   = 0;
129#endif	/* IP_RECVDSTADDR */
130				msg.msg_flags        = 0;			/* flags returned here */
131
132				nread = recvmsg(sockfd, &msg, 0);
133#endif	/* HAVE_MSGHDR_MSG_CONTROL */
134				if (nread < 0)
135					err_sys("datagram receive error");
136
137				if (verbose) {
138					printf("from %s", INET_NTOA(cliaddr.sin_addr));
139#ifdef	HAVE_MSGHDR_MSG_CONTROL
140#ifdef	IP_RECVDSTADDR
141					if (recvdstaddr) {
142						if (cmptr->cmsg_len != CONTROLLEN)
143							err_quit("control length (%d) != %d",
144								 cmptr->cmsg_len, CONTROLLEN);
145						if (cmptr->cmsg_level != IPPROTO_IP)
146							err_quit("control level != IPPROTO_IP");
147						if (cmptr->cmsg_type != IP_RECVDSTADDR)
148							err_quit("control type != IP_RECVDSTADDR");
149						bcopy(CMSG_DATA(cmptr), &dstinaddr,
150						      sizeof(struct in_addr));
151						bzero(cmptr, CONTROLLEN);
152
153						printf(", to %s", INET_NTOA(dstinaddr));
154					}
155#endif	/* IP_RECVDSTADDR */
156#endif	/* HAVE_MSGHDR_MSG_CONTROL */
157					printf(": ");
158					fflush(stdout);
159				}
160
161#ifdef	MSG_TRUNC
162				if (msg.msg_flags & MSG_TRUNC)
163					printf("(datagram truncated)\n");
164#endif
165
166			} else if (connectudp) {
167				/* msgpeek = 0 or MSG_PEEK */
168				flags = msgpeek;
169			oncemore:
170				if ( (nread = recv(sockfd, rbuf, readlen, flags)) < 0)
171					err_sys("recv error");
172				else if (nread == 0) {
173					if (verbose)
174						fprintf(stderr, "connection closed by peer\n");
175					break;		/* EOF, terminate */
176				}
177
178			} else {
179				/* Must use recvfrom() for unconnected UDP client */
180				servlen = sizeof(servaddr);
181				nread = recvfrom(sockfd, rbuf, readlen, 0,
182						 (struct sockaddr *) &servaddr, &servlen);
183				if (nread < 0)
184					err_sys("datagram recvfrom() error");
185
186				if (verbose) {
187					printf("from %s", INET_NTOA(servaddr.sin_addr));
188					printf(": ");
189					fflush(stdout);
190				}
191			}
192
193			if (crlf) {
194				ntowrite = crlf_strip(wbuf, writelen, rbuf, nread);
195				if (writen(STDOUT_FILENO, wbuf, ntowrite) != ntowrite)
196					err_sys("writen error to stdout");
197			} else {
198				if (writen(STDOUT_FILENO, rbuf, nread) != nread)
199					err_sys("writen error to stdout");
200			}
201
202			if (flags != 0) {
203				flags = 0;		/* no infinite loop */
204				goto oncemore;	/* read the message again */
205			}
206		}
207	}
208
209	if (pauseclose) {
210		if (verbose)
211			fprintf(stderr, "pausing before close\n");
212		sleep_us(pauseclose*1000);
213	}
214
215	if (close(sockfd) < 0)
216		err_sys("close error");		/* since SO_LINGER may be set */
217}
218