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