1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2  * This program is public domain.
3  *
4  * Socket routines.
5  */
6 
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <netdb.h>
13 #include <netinet/in.h>
14 #include <sys/socket.h>
15 #include <sys/time.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include "sockets.h"
19 #include "tetrinet.h"
20 
21 static FILE *logfile;
22 
23 /*************************************************************************/
24 
25 static int lastchar = EOF;
26 
sgetc(int s)27 int sgetc(int s)
28 {
29     int c;
30     char ch;
31 
32     if (lastchar != EOF) {
33 	c = lastchar;
34 	lastchar = EOF;
35 	return c;
36     }
37     if (read(s, &ch, 1) != 1)
38 	return EOF;
39     c = ch & 0xFF;
40     return c;
41 }
42 
sungetc(int c,int s)43 int sungetc(int c, int s)
44 {
45     return lastchar = c;
46 }
47 
48 /*************************************************************************/
49 
50 /* Read a string, stopping with (and discarding) 0xFF as line terminator.
51  * If connection was broken, return NULL.
52  */
53 
sgets(char * buf,int len,int s)54 char *sgets(char *buf, int len, int s)
55 {
56     int c;
57     unsigned char *ptr = (unsigned char *) buf;
58 
59     if (len == 0)
60 	return NULL;
61     c = sgetc(s);
62     while (--len && (*ptr++ = c) != 0xFF && (c = sgetc(s)) >= 0)
63 	;
64     if (c < 0)
65 	return NULL;
66     if (c == 0xFF)
67 	ptr--;
68     *ptr = 0;
69     if (log) {
70 	if (!logfile)
71 	    logfile = fopen(logname, "a");
72 	if (logfile) {
73 	    struct timeval tv;
74 	    gettimeofday(&tv, NULL);
75 	    fprintf(logfile, "[%d.%03d] <<< %s\n",
76 			(int) tv.tv_sec, (int) tv.tv_usec/1000, buf);
77 	    fflush(logfile);
78 	}
79     }
80     return buf;
81 }
82 
83 /*************************************************************************/
84 
85 /* Adds a 0xFF line terminator. */
86 
sputs(const char * str,int s)87 int sputs(const char *str, int s)
88 {
89     unsigned char c = 0xFF;
90     int n = 0;
91 
92     if (log) {
93 	if (!logfile)
94 	    logfile = fopen(logname, "a");
95 	if (logfile) {
96 	    struct timeval tv;
97 	    gettimeofday(&tv, NULL);
98 	    fprintf(logfile, "[%d.%03d] >>> %s\n",
99 			(int) tv.tv_sec, (int) tv.tv_usec/1000, str);
100 	}
101     }
102     if (*str != 0) {
103 	n = write(s, str, strlen(str));
104 	if (n <= 0)
105 	    return n;
106     }
107     if (write(s, &c, 1) <= 0)
108 	return n;
109     return n+1;
110 }
111 
112 /*************************************************************************/
113 
114 /* Adds a 0xFF line terminator. */
115 
sockprintf(int s,const char * fmt,...)116 int sockprintf(int s, const char *fmt, ...)
117 {
118     va_list args;
119     char buf[16384];	/* Really huge, to try and avoid truncation */
120 
121     va_start(args, fmt);
122     vsnprintf(buf, sizeof(buf), fmt, args);
123     return sputs(buf, s);
124 }
125 
126 /*************************************************************************/
127 /*************************************************************************/
128 
conn(const char * host,int port,char ipbuf[4])129 int conn(const char *host, int port, char ipbuf[4])
130 {
131 #ifdef HAVE_IPV6
132     char hbuf[NI_MAXHOST];
133     struct addrinfo hints, *res, *res0;
134     char service[11];
135 #else
136     struct hostent *hp;
137     struct sockaddr_in sa;
138 #endif
139     int sock = -1;
140 
141 #ifdef HAVE_IPV6
142     snprintf(service, sizeof(service), "%d", port);
143     memset(&hints, 0, sizeof(hints));
144     hints.ai_family = AF_UNSPEC;
145     hints.ai_socktype = SOCK_STREAM;
146     if (getaddrinfo(host, service, &hints, &res0))
147 	return -1;
148     for (res = res0; res; res = res->ai_next) {
149 	int errno_save;
150 	sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
151 	if (sock < 0)
152 	    continue;
153 	getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
154 		    NULL, 0, 0);
155 	if (connect(sock, res->ai_addr, res->ai_addrlen) == 0) {
156 	    if (ipbuf) {
157 		if (res->ai_family == AF_INET6) {
158 		    struct sockaddr_in6 *sin6 =
159 			(struct sockaddr_in6 *)(res->ai_addr);
160 		    memcpy(ipbuf, (char *)(&sin6->sin6_addr) + 12, 4);
161 		} else {
162 		    struct sockaddr_in *sin =
163 			(struct sockaddr_in *)(res->ai_addr);
164 		    memcpy(ipbuf, &sin->sin_addr, 4);
165 		}
166 	    }
167 	    break;
168 	}
169 	errno_save = errno;
170 	close(sock);
171 	sock = -1;
172 	errno = errno_save;
173     }
174     freeaddrinfo(res0);
175 #else  /* !HAVE_IPV6 */
176     memset(&sa, 0, sizeof(sa));
177     if (!(hp = gethostbyname(host)))
178 	return -1;
179     memcpy((char *)&sa.sin_addr, hp->h_addr, hp->h_length);
180     sa.sin_family = hp->h_addrtype;
181     sa.sin_port = htons((unsigned short)port);
182     if ((sock = socket(sa.sin_family, SOCK_STREAM, 0)) < 0)
183 	return -1;
184     if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
185 	int errno_save = errno;
186 	close(sock);
187 	errno = errno_save;
188 	return -1;
189     }
190     if (ipbuf)
191 	memcpy(retbuf, &sa.sin_addr, 4);
192 #endif
193 
194     return sock;
195 }
196 
197 /*************************************************************************/
198 
disconn(int s)199 void disconn(int s)
200 {
201     shutdown(s, 2);
202     close(s);
203 }
204 
205 /*************************************************************************/
206