1// This file is part of SmallBASIC
2//
3// Network library (byte-stream sockets)
4//
5// This program is distributed under the terms of the GPL v2.0 or later
6// Download the GNU Public License (GPL) from www.gnu.org
7//
8// Copyright(C) 2000 Nicholas Christopoulos
9
10#include "common/sys.h"
11#include "common/inet.h"
12#include "common/device.h"
13
14#ifdef HAVE_CONFIG_H
15#include <config.h>
16#endif
17
18#if defined(_Win32)
19static int inetlib_init = 0;
20#else
21#include <arpa/inet.h>
22#include <sys/ioctl.h>
23#include <netdb.h>
24#include <netinet/in.h>
25#include <signal.h>
26#endif
27
28// the length of time (usec) to block waiting for an event
29#define BLOCK_INTERVAL 250000
30
31/**
32 * prepare to use the network
33 */
34int net_init() {
35#if defined(_Win32)
36 if (!inetlib_init) {
37 inetlib_init = 1;
38 WSADATA wsadata;
39 if (WSAStartup(MAKEWORD(2, 0), &wsadata)) {
40 return 0;
41 }
42 return 1;
43 }
44#else
45 struct sigaction sa;
46 sa.sa_handler = SIG_IGN;
47 sigemptyset(&sa.sa_mask);
48 sa.sa_flags = 0;
49 sigaction(SIGPIPE, &sa, 0);
50#endif
51 return 1;
52}
53
54/**
55 * stop using the network
56 */
57int net_close() {
58#if defined(_Win32)
59 if (inetlib_init) {
60 WSACleanup();
61 inetlib_init = 0;
62 }
63#endif
64 return 1;
65}
66
67/**
68 * sends a string to socket
69 */
70void net_print(socket_t s, const char *str) {
71 send(s, str, strlen(str), 0);
72}
73
74void net_send(socket_t s, const char *str, size_t size) {
75 send(s, str, size, 0);
76}
77
78/**
79 * sends a string to socket
80 */
81void net_printf(socket_t s, const char *fmt, ...) {
82 char buf[1025];
83 va_list argp;
84
85 va_start(argp, fmt);
86 vsnprintf(buf, sizeof(buf), fmt, argp);
87 va_end(argp);
88 net_print(s, buf);
89}
90
91/**
92 * read the specified number of bytes from the socket
93 */
94int net_read(socket_t s, char *buf, int size) {
95 fd_set readfds;
96 struct timeval tv;
97
98 // clear the set
99 FD_ZERO(&readfds);
100
101 while (1) {
102 FD_SET(s, &readfds);
103 tv.tv_sec = 0;
104 tv.tv_usec = BLOCK_INTERVAL;
105
106 int rv = select(s + 1, &readfds, NULL, NULL, &tv);
107 if (rv == -1) {
108 // an error occured
109 return 0;
110 } else if (rv == 0) {
111 // timeout occured
112 if (0 != dev_events(0)) {
113 s = 0;
114 break;
115 }
116 } else {
117 // ready to read
118 return recv(s, buf, size, 0);
119 }
120 }
121 return 0;
122}
123
124/**
125 * read a string from a socket until a char from delim str found.
126 */
127int net_input(socket_t s, char *buf, int size, const char *delim) {
128 // wait for remote input without eating cpu
129 fd_set readfds;
130 struct timeval tv;
131 char ch;
132 int count = 0;
133
134 // clear the set
135 FD_ZERO(&readfds);
136
137 while (1) {
138 tv.tv_sec = 0;
139 tv.tv_usec = BLOCK_INTERVAL; // time is reset in select() call in linux
140 FD_SET(s, &readfds);
141
142 int rv = select(s + 1, &readfds, NULL, NULL, &tv);
143 if (rv == -1) {
144 return 0; // an error occured
145 } else if (rv == 0) {
146 // timeout occured - check for program break
147 if (0 != dev_events(0)) {
148 return 0;
149 }
150 } else if (FD_ISSET(s, &readfds)) {
151 // ready for reading
152 break;
153 }
154 }
155
156 FD_ZERO(&readfds);
157
158 memset(buf, 0, size);
159 while (count < size) {
160 int bytes = net_read(s, &ch, 1);
161 if (bytes <= 0) {
162 return count; // no more data
163 } else {
164 if (ch == 0) {
165 return count;
166 }
167 if (delim) {
168 if ((strchr(delim, ch) != NULL)) {
169 return count; // delimiter found
170 }
171 }
172 buf[count] = ch;
173 count += bytes;
174 }
175 }
176
177 return count;
178}
179
180/**
181 * return true if there something waiting
182 */
183int net_peek(socket_t s) {
184#if defined(_Win32)
185 unsigned long bytes;
186
187 ioctlsocket(s, FIONREAD, &bytes);
188 return (bytes > 0);
189#else
190 int bytes;
191
192 ioctl(s, FIONREAD, &bytes);
193 return (bytes > 0);
194#endif
195}
196
197/**
198 * connect to server and returns the socket
199 */
200socket_t net_connect(const char *server_name, int server_port) {
201 socket_t sock;
202 uint32_t inaddr;
203 struct sockaddr_in ad;
204
205 net_init();
206
207 memset(&ad, 0, sizeof(ad));
208 ad.sin_family = AF_INET;
209
210 if ((inaddr = inet_addr(server_name)) == INADDR_NONE) {
211 struct hostent *hp = gethostbyname(server_name);
212 if (hp == NULL) {
213 return -1;
214 }
215 memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
216 } else {
217 memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
218 }
219
220 ad.sin_port = htons(server_port);
221 sock = socket(AF_INET, SOCK_STREAM, 0);
222 if (sock <= 0) {
223 return sock;
224 }
225 if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0) {
226 net_disconnect(sock);
227 return -1;
228 }
229 return sock;
230}
231
232/**
233 * listen for an incoming connection on the given port and
234 * returns the socket once a connection has been established
235 */
236socket_t net_listen(int server_port) {
237 int listener;
238 struct sockaddr_in addr, remoteaddr;
239 socket_t s;
240 fd_set readfds;
241 struct timeval tv;
242 int rv;
243 int yes = 1;
244
245 // more info about listen sockets:
246 // http://beej.us/guide/bgnet/output/htmlsingle/bgnet.html#acceptman
247 net_init();
248 listener = socket(PF_INET, SOCK_STREAM, 0);
249 if (listener <= 0) {
250 return listener;
251 }
252
253 addr.sin_family = AF_INET;
254 addr.sin_port = htons(server_port); // clients connect to this port
255 addr.sin_addr.s_addr = INADDR_ANY; // autoselect IP address
256 memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
257
258 // prevent address already in use bind errors
259 if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(int)) == -1) {
260 net_disconnect(listener);
261 return -1;
262 }
263 // set s up to be a server (listening) socket
264 if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
265 net_disconnect(listener);
266 return -1;
267 }
268
269 if (listen(listener, 1) == -1) {
270 net_disconnect(listener);
271 return -1;
272 }
273 // clear the set
274 FD_ZERO(&readfds);
275
276 while (1) {
277 tv.tv_sec = 0; // block at 1 second intervals
278 tv.tv_usec = 500000; // time is reset in select() call in linux
279 FD_SET(listener, &readfds);
280
281 rv = select(listener + 1, &readfds, NULL, NULL, &tv);
282 if (rv == -1) {
283 s = 0;
284 break; // an error occured
285 } else if (rv == 0) {
286 // timeout occured - check for program break
287 if (0 != dev_events(0)) {
288 s = 0;
289 break;
290 }
291 } else if (FD_ISSET(listener, &readfds)) {
292 // connection is ready
293#if defined(_Win32)
294 int remoteaddr_len = sizeof(remoteaddr);
295#else
296 socklen_t remoteaddr_len = sizeof(remoteaddr);
297#endif
298 s = accept(listener, (struct sockaddr *)&remoteaddr, &remoteaddr_len);
299 break;
300 }
301 }
302
303 FD_ZERO(&readfds);
304 net_disconnect(listener);
305
306 return s;
307}
308
309/**
310 * disconnect the given network connection
311 */
312void net_disconnect(socket_t s) {
313 if (s != -1) {
314#if defined(_Win32)
315 closesocket(s);
316#else
317 close(s);
318#endif
319 }
320}
321
322