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) |
19 | static 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 | */ |
34 | int 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 | */ |
57 | int 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 | */ |
70 | void net_print(socket_t s, const char *str) { |
71 | send(s, str, strlen(str), 0); |
72 | } |
73 | |
74 | void 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 | */ |
81 | void 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 | */ |
94 | int 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 | */ |
127 | int 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 | */ |
183 | int 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 | */ |
200 | socket_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 | */ |
236 | socket_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 | */ |
312 | void 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 | |