1 /*
2 * Copyright (C) 1997-2004, Michael Jennings
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies of the Software, its documentation and marketing & publicity
13 * materials, and acknowledgment shall be given in the documentation, materials
14 * and software packages that this Software was used.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 static const char __attribute__((unused)) cvs_ident[] = "$Id: socket.c,v 1.23 2004/07/23 21:38:39 mej Exp $";
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include <libast_internal.h>
31
32 static spif_url_t spif_url_new_from_ipaddr(spif_ipsockaddr_t);
33 static spif_bool_t spif_url_init_from_ipaddr(spif_url_t, spif_ipsockaddr_t);
34 static spif_url_t spif_url_new_from_unixaddr(spif_unixsockaddr_t);
35 static spif_bool_t spif_url_init_from_unixaddr(spif_url_t, spif_unixsockaddr_t);
36 static spif_ipsockaddr_t spif_url_get_ipaddr(spif_url_t);
37 static spif_unixsockaddr_t spif_url_get_unixaddr(spif_url_t);
38 static spif_sockport_t spif_url_get_portnum(spif_url_t);
39 static spif_bool_t spif_socket_get_proto(spif_socket_t);
40
41 /* *INDENT-OFF* */
42 static SPIF_CONST_TYPE(class) s_class = {
43 SPIF_DECL_CLASSNAME(socket),
44 (spif_func_t) spif_socket_new,
45 (spif_func_t) spif_socket_init,
46 (spif_func_t) spif_socket_done,
47 (spif_func_t) spif_socket_del,
48 (spif_func_t) spif_socket_show,
49 (spif_func_t) spif_socket_comp,
50 (spif_func_t) spif_socket_dup,
51 (spif_func_t) spif_socket_type
52 };
53 SPIF_TYPE(class) SPIF_CLASS_VAR(socket) = &s_class;
54 /* *INDENT-ON* */
55
56 spif_socket_t
spif_socket_new(void)57 spif_socket_new(void)
58 {
59 spif_socket_t self;
60
61 self = SPIF_ALLOC(socket);
62 if (!spif_socket_init(self)) {
63 SPIF_DEALLOC(self);
64 self = SPIF_NULL_TYPE(socket);
65 }
66 return self;
67 }
68
69 spif_socket_t
spif_socket_new_from_urls(spif_url_t surl,spif_url_t durl)70 spif_socket_new_from_urls(spif_url_t surl, spif_url_t durl)
71 {
72 spif_socket_t self;
73
74 self = SPIF_ALLOC(socket);
75 if (!spif_socket_init_from_urls(self, surl, durl)) {
76 SPIF_DEALLOC(self);
77 self = SPIF_NULL_TYPE(socket);
78 }
79 return self;
80 }
81
82 spif_bool_t
spif_socket_init(spif_socket_t self)83 spif_socket_init(spif_socket_t self)
84 {
85 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
86 /* ***NOT NEEDED*** spif_obj_init(SPIF_OBJ(self)); */
87 spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS_VAR(socket));
88 self->fd = -1;
89 self->fam = AF_INET;
90 self->type = SOCK_STREAM;
91 self->proto = 0;
92 self->addr = SPIF_NULL_TYPE(sockaddr);
93 self->len = 0;
94 self->flags = 0;
95 self->local_url = SPIF_NULL_TYPE(url);
96 self->remote_url = SPIF_NULL_TYPE(url);
97 return TRUE;
98 }
99
100 spif_bool_t
spif_socket_init_from_urls(spif_socket_t self,spif_url_t surl,spif_url_t durl)101 spif_socket_init_from_urls(spif_socket_t self, spif_url_t surl, spif_url_t durl)
102 {
103 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
104 /* ***NOT NEEDED*** spif_obj_init(SPIF_OBJ(self)); */
105 spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS_VAR(socket));
106 self->fd = -1;
107 self->fam = AF_INET;
108 self->type = SOCK_STREAM;
109 self->proto = 0;
110 self->addr = SPIF_NULL_TYPE(sockaddr);
111 self->len = 0;
112 self->flags = 0;
113 if (!SPIF_URL_ISNULL(surl)) {
114 self->local_url = spif_url_dup(surl);
115 } else {
116 self->local_url = SPIF_NULL_TYPE(url);
117 }
118 if (!SPIF_URL_ISNULL(durl)) {
119 self->remote_url = spif_url_dup(durl);
120 } else {
121 self->remote_url = SPIF_NULL_TYPE(url);
122 }
123 return TRUE;
124 }
125
126 spif_bool_t
spif_socket_done(spif_socket_t self)127 spif_socket_done(spif_socket_t self)
128 {
129 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
130 if (self->fd >= 0) {
131 spif_socket_close(self);
132 }
133 self->fam = AF_INET;
134 self->type = SOCK_STREAM;
135 self->proto = 0;
136 if (self->addr != SPIF_NULL_TYPE(sockaddr)) {
137 SPIF_DEALLOC(self->addr);
138 self->addr = SPIF_NULL_TYPE(sockaddr);
139 }
140 self->len = 0;
141 self->flags = 0;
142 if (!SPIF_URL_ISNULL(self->local_url)) {
143 spif_url_del(self->local_url);
144 self->local_url = SPIF_NULL_TYPE(url);
145 }
146 if (!SPIF_URL_ISNULL(self->remote_url)) {
147 spif_url_del(self->remote_url);
148 self->remote_url = SPIF_NULL_TYPE(url);
149 }
150 return TRUE;
151 }
152
153 spif_bool_t
spif_socket_del(spif_socket_t self)154 spif_socket_del(spif_socket_t self)
155 {
156 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
157 spif_socket_done(self);
158 SPIF_DEALLOC(self);
159 return TRUE;
160 }
161
162 spif_str_t
spif_socket_show(spif_socket_t self,spif_charptr_t name,spif_str_t buff,size_t indent)163 spif_socket_show(spif_socket_t self, spif_charptr_t name, spif_str_t buff, size_t indent)
164 {
165 spif_char_t tmp[4096];
166
167 if (SPIF_SOCKET_ISNULL(self)) {
168 SPIF_OBJ_SHOW_NULL(socket, name, buff, indent, tmp);
169 return buff;
170 }
171
172 memset(tmp, ' ', indent);
173 snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent,
174 "(spif_socket_t) %s: %10p {\n",
175 name, SPIF_CAST(ptr) self);
176 if (SPIF_STR_ISNULL(buff)) {
177 buff = spif_str_new_from_ptr(tmp);
178 } else {
179 spif_str_append_from_ptr(buff, tmp);
180 }
181
182 indent += 2;
183 memset(tmp, ' ', indent);
184 snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_sockfd_t) fd: %d\n", self->fd);
185 spif_str_append_from_ptr(buff, tmp);
186
187 snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_sockfamily_t) fam: %d\n", (int) self->fam);
188 spif_str_append_from_ptr(buff, tmp);
189
190 snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_socktype_t) type: %d\n", (int) self->type);
191 spif_str_append_from_ptr(buff, tmp);
192
193 snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_sockproto_t) proto: %d\n", (int) self->proto);
194 spif_str_append_from_ptr(buff, tmp);
195
196 snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_sockaddr_t) addr: %10p\n", SPIF_CAST(ptr) self->addr);
197 spif_str_append_from_ptr(buff, tmp);
198
199 snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_sockaddr_len_t) len: %lu\n", (unsigned long) self->len);
200 spif_str_append_from_ptr(buff, tmp);
201
202 snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_uint32_t) flags: 0x%08x\n", (unsigned) self->flags);
203 spif_str_append_from_ptr(buff, tmp);
204
205 spif_url_show(self->local_url, SPIF_CHARPTR("local_url"), buff, indent);
206 spif_url_show(self->remote_url, SPIF_CHARPTR("remote_url"), buff, indent);
207
208 indent -= 2;
209 snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "}\n");
210 spif_str_append_from_ptr(buff, tmp);
211
212 return buff;
213 }
214
215 spif_cmp_t
spif_socket_comp(spif_socket_t self,spif_socket_t other)216 spif_socket_comp(spif_socket_t self, spif_socket_t other)
217 {
218 return SPIF_CMP_FROM_INT(self->fd - other->fd);
219 }
220
221 spif_socket_t
spif_socket_dup(spif_socket_t self)222 spif_socket_dup(spif_socket_t self)
223 {
224 spif_socket_t tmp;
225
226 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), SPIF_NULL_TYPE(socket));
227 tmp = spif_socket_new();
228 if (self->fd >= 0) {
229 tmp->fd = dup(self->fd);
230 }
231 tmp->fam = self->fam;
232 tmp->type = self->type;
233 tmp->proto = self->proto;
234 tmp->len = self->len;
235 if (self->addr != SPIF_NULL_TYPE(sockaddr)) {
236 tmp->addr = SPIF_CAST(sockaddr) MALLOC(tmp->len);
237 memcpy(tmp->addr, self->addr, tmp->len);
238 }
239 tmp->flags = self->flags;
240 if (!SPIF_URL_ISNULL(self->local_url)) {
241 tmp->local_url = spif_url_dup(self->local_url);
242 }
243 if (!SPIF_URL_ISNULL(self->remote_url)) {
244 tmp->remote_url = spif_url_dup(self->remote_url);
245 }
246 return tmp;
247 }
248
249 spif_classname_t
spif_socket_type(spif_socket_t self)250 spif_socket_type(spif_socket_t self)
251 {
252 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), SPIF_NULL_TYPE(classname));
253 return SPIF_OBJ_CLASSNAME(self);
254 }
255
256 spif_bool_t
spif_socket_open(spif_socket_t self)257 spif_socket_open(spif_socket_t self)
258 {
259 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
260
261 if (!(self->addr)) {
262 /* Set family, protocol, and type flags. */
263 spif_socket_get_proto(self);
264
265 /* Get remote address, if we have one. */
266 if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_INET)) {
267 self->fam = AF_INET;
268 if (!SPIF_URL_ISNULL(self->remote_url)) {
269 self->addr = SPIF_CAST(sockaddr) spif_url_get_ipaddr(self->remote_url);
270 } else {
271 self->addr = SPIF_NULL_TYPE(sockaddr);
272 }
273 if (self->addr == SPIF_NULL_TYPE(sockaddr)) {
274 self->len = 0;
275 } else {
276 self->len = SPIF_SIZEOF_TYPE(ipsockaddr);
277 }
278 } else if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_UNIX)) {
279 self->fam = AF_UNIX;
280 if (!SPIF_URL_ISNULL(self->remote_url)) {
281 self->addr = SPIF_CAST(sockaddr) spif_url_get_unixaddr(self->remote_url);
282 } else {
283 self->addr = SPIF_NULL_TYPE(sockaddr);
284 }
285 if (self->addr == SPIF_NULL_TYPE(sockaddr)) {
286 self->len = 0;
287 } else {
288 self->len = SPIF_SIZEOF_TYPE(unixsockaddr);
289 }
290 } else {
291 D_OBJ(("Unknown socket family 0x%08x!\n", SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_FAMILY)));
292 ASSERT_NOTREACHED_RVAL(FALSE);
293 }
294 }
295
296 /* If the socket isn't open yet, open it and get a file descriptor. */
297 if (self->fd < 0) {
298 if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_TYPE_STREAM)) {
299 self->type = SOCK_STREAM;
300 } else if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_TYPE_DGRAM)) {
301 self->type = SOCK_DGRAM;
302 } else if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_TYPE_RAW)) {
303 self->type = SOCK_RAW;
304 } else {
305 D_OBJ(("Unknown socket type 0x%08x!\n", SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_TYPE)));
306 ASSERT_NOTREACHED_RVAL(FALSE);
307 }
308
309 self->fd = SPIF_CAST(sockfd) socket(self->fam, self->type, self->proto);
310 if (self->fd < 0) {
311 libast_print_error("Unable to create socket(%d, %d, %d) -- %s\n", (int) self->fam,
312 (int) self->type, (int) self->proto, strerror(errno));
313 return FALSE;
314 }
315
316 /* If we have a local URL, bind it to the appropriate address and port. */
317 if (!SPIF_URL_ISNULL(self->local_url)) {
318 if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_INET)) {
319 spif_ipsockaddr_t addr;
320
321 addr = spif_url_get_ipaddr(self->local_url);
322
323 D_OBJ(("Binding to port %d\n", ntohs(addr->sin_port)));
324 if (bind(self->fd, SPIF_CAST(sockaddr) addr, SPIF_SIZEOF_TYPE(ipsockaddr))) {
325 libast_print_error("Unable to bind socket %d to %s -- %s\n", (int) self->fd,
326 SPIF_STR_STR(self->local_url), strerror(errno));
327 FREE(addr);
328 return FALSE;
329 }
330 FREE(addr);
331 } else if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_UNIX)) {
332 spif_unixsockaddr_t addr;
333
334 addr = spif_url_get_unixaddr(self->local_url);
335
336 if (bind(self->fd, SPIF_CAST(sockaddr) addr, SPIF_SIZEOF_TYPE(unixsockaddr))) {
337 libast_print_error("Unable to bind socket %d to %s -- %s\n", (int) self->fd,
338 SPIF_STR_STR(self->local_url), strerror(errno));
339 FREE(addr);
340 return FALSE;
341 }
342 FREE(addr);
343 }
344 }
345 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_OPEN);
346 }
347
348 /* Connect if we have a destination. If not, open a listening socket. */
349 if (!SPIF_URL_ISNULL(self->remote_url)) {
350 spif_socket_clear_nbio(self);
351 if ((connect(self->fd, self->addr, self->len)) < 0) {
352 libast_print_error("Unable to connect socket %d to %s -- %s\n", (int) self->fd,
353 SPIF_STR_STR(self->remote_url), strerror(errno));
354 return FALSE;
355 }
356 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_CONNECTED);
357 } else if (!SPIF_URL_ISNULL(self->local_url)) {
358 if ((listen(self->fd, 5)) < 0) {
359 libast_print_error("Unable to listen at %s on socket %d -- %s\n",
360 SPIF_STR_STR(self->local_url), (int) self->fd, strerror(errno));
361 return FALSE;
362 }
363 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_LISTEN);
364 }
365
366 return TRUE;
367 }
368
369 spif_bool_t
spif_socket_close(spif_socket_t self)370 spif_socket_close(spif_socket_t self)
371 {
372 int ret;
373
374 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
375 REQUIRE_RVAL(self->fd >= 0, FALSE);
376 SPIF_SOCKET_FLAGS_CLEAR(self, SPIF_SOCKET_FLAGS_IOSTATE);
377 do {
378 ret = close(self->fd);
379 } while ((ret < 0) && (errno == EINTR));
380 if (ret < 0) {
381 libast_print_error("Unable to close socket %d -- %s\n", self->fd, strerror(errno));
382 self->fd = -1;
383 return FALSE;
384 }
385 self->fd = -1;
386 return TRUE;
387 }
388
389 spif_bool_t
spif_socket_check_io(spif_socket_t self)390 spif_socket_check_io(spif_socket_t self)
391 {
392 static struct timeval tv = { 0, 0 };
393 fd_set read_fds, write_fds;
394
395 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
396 REQUIRE_RVAL(self->fd >= 0, FALSE);
397
398 FD_ZERO(&read_fds);
399 FD_SET(self->fd, &read_fds);
400 FD_ZERO(&write_fds);
401 FD_SET(self->fd, &write_fds);
402 if ((select(self->fd + 1, &read_fds, &write_fds, NULL, &tv)) < 0) {
403 libast_print_error("Unable to select() on %d -- %s\n", self->fd, strerror(errno));
404 return FALSE;
405 }
406
407 if (FD_ISSET(self->fd, &read_fds)) {
408 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_HAVE_INPUT);
409 } else {
410 SPIF_SOCKET_FLAGS_CLEAR(self, SPIF_SOCKET_FLAGS_HAVE_INPUT);
411 }
412 if (FD_ISSET(self->fd, &write_fds)) {
413 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_CAN_OUTPUT);
414 } else {
415 SPIF_SOCKET_FLAGS_CLEAR(self, SPIF_SOCKET_FLAGS_CAN_OUTPUT);
416 }
417 return TRUE;
418 }
419
420 spif_socket_t
spif_socket_accept(spif_socket_t self)421 spif_socket_accept(spif_socket_t self)
422 {
423 spif_sockaddr_t addr;
424 spif_sockaddr_len_t len;
425 int newfd;
426 spif_socket_t tmp;
427
428 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), SPIF_NULL_TYPE(socket));
429
430 addr = SPIF_ALLOC(sockaddr);
431 len = SPIF_SIZEOF_TYPE(sockaddr);
432 do {
433 newfd = accept(self->fd, addr, &len);
434 } while ((newfd < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)));
435
436 if (newfd < 0) {
437 libast_print_error("Unable to accept() connection on %d -- %s\n", self->fd, strerror(errno));
438 return SPIF_NULL_TYPE(socket);
439 }
440
441 /* We got one. Create and return a new socket object for the accepted connection. */
442 tmp = spif_socket_dup(self);
443 tmp->fd = newfd;
444 SPIF_SOCKET_FLAGS_CLEAR(tmp, (SPIF_SOCKET_FLAGS_LISTEN | SPIF_SOCKET_FLAGS_HAVE_INPUT | SPIF_SOCKET_FLAGS_CAN_OUTPUT));
445 if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_INET)) {
446 tmp->remote_url = spif_url_new_from_ipaddr(SPIF_CAST(ipsockaddr) addr);
447 } else if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_UNIX)) {
448 tmp->remote_url = spif_url_new_from_unixaddr(SPIF_CAST(unixsockaddr) addr);
449 }
450 SPIF_DEALLOC(addr);
451 if (SPIF_SOCKET_FLAGS_IS_SET(self, SPIF_SOCKET_FLAGS_NBIO)) {
452 spif_socket_set_nbio(tmp);
453 }
454 return tmp;
455 }
456
457 spif_bool_t
spif_socket_send(spif_socket_t self,spif_str_t data)458 spif_socket_send(spif_socket_t self, spif_str_t data)
459 {
460 size_t len;
461 int num_written;
462 struct timeval tv = { 0, 0 };
463
464 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
465 REQUIRE_RVAL(!SPIF_STR_ISNULL(data), FALSE);
466
467 len = spif_str_get_len(data);
468 REQUIRE_RVAL(len > 0, FALSE);
469
470 num_written = write(self->fd, SPIF_STR_STR(data), len);
471 for (; (num_written < 0) && ((errno == EAGAIN) || (errno == EINTR)); ) {
472 tv.tv_usec += 10000;
473 if (tv.tv_usec == 1000000) {
474 tv.tv_usec = 0;
475 tv.tv_sec++;
476 }
477 select(0, NULL, NULL, NULL, &tv);
478 num_written = write(self->fd, SPIF_STR_STR(data), len);
479 }
480 if (num_written < 0) {
481 D_OBJ(("Unable to write to socket %d -- %s\n", self->fd, strerror(errno)));
482 switch (errno) {
483 case EFBIG:
484 {
485 spif_bool_t b;
486 spif_str_t tmp_buf;
487 spif_charptr_t s;
488 long left;
489
490 for (left = len, s = SPIF_CHARPTR(SPIF_STR_STR(data)); left > 0; s += 1024, left -= 1024) {
491 tmp_buf = spif_str_new_from_buff(s, 1024);
492 b = spif_socket_send(self, tmp_buf);
493 if (b == FALSE) {
494 spif_str_del(tmp_buf);
495 return b;
496 }
497 }
498 }
499 break;
500 case EIO:
501 case EPIPE:
502 close(self->fd);
503 /* Drop */
504 case EBADF:
505 case EINVAL:
506 default:
507 self->fd = -1;
508 SPIF_SOCKET_FLAGS_CLEAR(self, SPIF_SOCKET_FLAGS_IOSTATE);
509 return FALSE;
510 break;
511 }
512 }
513 return TRUE;
514 }
515
516 spif_str_t
spif_socket_recv(spif_socket_t self)517 spif_socket_recv(spif_socket_t self)
518 {
519 spif_str_t new_str;
520
521 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), SPIF_NULL_TYPE(str));
522 new_str = spif_str_new_from_fd(self->fd);
523 return new_str;
524 }
525
526 spif_bool_t
spif_socket_set_nbio(spif_socket_t self)527 spif_socket_set_nbio(spif_socket_t self)
528 {
529 int flags;
530
531 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
532 REQUIRE_RVAL(self->fd >= 0, FALSE);
533
534 /* Set the socket options for non-blocking I/O. */
535 #if defined(O_NDELAY)
536 flags = fcntl(self->fd, F_GETFL, 0);
537 if (flags < 0) {
538 flags = O_NDELAY;
539 } else {
540 flags |= O_NDELAY;
541 }
542 if ((fcntl(self->fd, F_SETFL, flags)) != 0) {
543 return FALSE;
544 } else {
545 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_NBIO);
546 return TRUE;
547 }
548 #elif defined(O_NONBLOCK)
549 flags = fcntl(self->fd, F_GETFL, 0);
550 if (flags < 0) {
551 flags = O_NONBLOCK;
552 } else {
553 flags |= O_NONBLOCK;
554 }
555 if ((fcntl(self->fd, F_SETFL, flags)) != 0) {
556 return FALSE;
557 } else {
558 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_NBIO);
559 return TRUE;
560 }
561 #else
562 flags = 1;
563 if ((ioctl(self->fd, FIONBIO, &flags)) != 0) {
564 return FALSE;
565 } else {
566 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_NBIO);
567 return TRUE;
568 }
569 #endif
570 }
571
572 spif_bool_t
spif_socket_clear_nbio(spif_socket_t self)573 spif_socket_clear_nbio(spif_socket_t self)
574 {
575 int flags;
576
577 ASSERT_RVAL(!SPIF_SOCKET_ISNULL(self), FALSE);
578 REQUIRE_RVAL(self->fd >= 0, FALSE);
579
580 /* Set the socket options for blocking I/O. */
581 #if defined(O_NDELAY)
582 flags = fcntl(self->fd, F_GETFL, 0);
583 if (flags < 0) {
584 flags = 0;
585 } else {
586 flags &= ~(O_NDELAY);
587 }
588 if ((fcntl(self->fd, F_SETFL, flags)) != 0) {
589 return FALSE;
590 } else {
591 SPIF_SOCKET_FLAGS_CLEAR(self, SPIF_SOCKET_FLAGS_NBIO);
592 return TRUE;
593 }
594 #elif defined(O_NONBLOCK)
595 flags = fcntl(self->fd, F_GETFL, 0);
596 if (flags < 0) {
597 flags = 0;
598 } else {
599 flags &= ~(O_NONBLOCK);
600 }
601 if ((fcntl(self->fd, F_SETFL, flags)) != 0) {
602 return FALSE;
603 } else {
604 SPIF_SOCKET_FLAGS_CLEAR(self, SPIF_SOCKET_FLAGS_NBIO);
605 return TRUE;
606 }
607 #else
608 flags = 0;
609 if ((ioctl(self->fd, FIONBIO, &flags)) != 0) {
610 return FALSE;
611 } else {
612 SPIF_SOCKET_FLAGS_CLEAR(self, SPIF_SOCKET_FLAGS_NBIO);
613 return TRUE;
614 }
615 #endif
616 }
617
618 /**************** Static internal functions ****************/
619
620 static spif_url_t
spif_url_new_from_ipaddr(spif_ipsockaddr_t ipaddr)621 spif_url_new_from_ipaddr(spif_ipsockaddr_t ipaddr)
622 {
623 spif_url_t self;
624
625 self = SPIF_ALLOC(url);
626 if (!spif_url_init_from_ipaddr(self, ipaddr)) {
627 SPIF_DEALLOC(self);
628 self = SPIF_NULL_TYPE(url);
629 }
630 return self;
631 }
632
633 static spif_bool_t
spif_url_init_from_ipaddr(spif_url_t self,spif_ipsockaddr_t ipaddr)634 spif_url_init_from_ipaddr(spif_url_t self, spif_ipsockaddr_t ipaddr)
635 {
636 spif_uint8_t tries;
637 spif_hostinfo_t hinfo;
638
639 ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
640 spif_str_init(SPIF_STR(self));
641 spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS_VAR(url));
642 self->proto = SPIF_NULL_TYPE(str);
643 self->user = SPIF_NULL_TYPE(str);
644 self->passwd = SPIF_NULL_TYPE(str);
645 self->path = SPIF_NULL_TYPE(str);
646 self->query = SPIF_NULL_TYPE(str);
647
648 /* Try up to 3 times to resolve the hostname. */
649 h_errno = 0;
650 tries = 0;
651 do {
652 tries++;
653 hinfo = gethostbyaddr(SPIF_CONST_CAST_C(char *) &(ipaddr->sin_addr), sizeof(ipaddr->sin_addr), AF_INET);
654 } while ((tries <= 3) && (hinfo == NULL) && (h_errno == TRY_AGAIN));
655 if (hinfo == NULL || hinfo->h_name == NULL) {
656 spif_charptr_t buff;
657
658 buff = SPIF_CHARPTR(inet_ntoa(ipaddr->sin_addr));
659 self->host = spif_str_new_from_ptr(buff);
660 } else {
661 self->host = spif_str_new_from_ptr(SPIF_CHARPTR(hinfo->h_name));
662 }
663
664 self->port = spif_str_new_from_num(ntohs(ipaddr->sin_port));
665 return TRUE;
666 }
667
668 static spif_url_t
spif_url_new_from_unixaddr(spif_unixsockaddr_t unixaddr)669 spif_url_new_from_unixaddr(spif_unixsockaddr_t unixaddr)
670 {
671 spif_url_t self;
672
673 self = SPIF_ALLOC(url);
674 if (!spif_url_init_from_unixaddr(self, unixaddr)) {
675 SPIF_DEALLOC(self);
676 self = SPIF_NULL_TYPE(url);
677 }
678 return self;
679 }
680
681 static spif_bool_t
spif_url_init_from_unixaddr(spif_url_t self,spif_unixsockaddr_t unixaddr)682 spif_url_init_from_unixaddr(spif_url_t self, spif_unixsockaddr_t unixaddr)
683 {
684 ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
685 spif_str_init(SPIF_STR(self));
686 spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS_VAR(url));
687 self->proto = SPIF_NULL_TYPE(str);
688 self->user = SPIF_NULL_TYPE(str);
689 self->passwd = SPIF_NULL_TYPE(str);
690 self->host = SPIF_NULL_TYPE(str);
691 self->port = SPIF_NULL_TYPE(str);
692 self->query = SPIF_NULL_TYPE(str);
693
694 if (unixaddr->sun_path != NULL) {
695 self->path = spif_str_new_from_ptr(SPIF_CHARPTR(unixaddr->sun_path));
696 } else {
697 self->path = SPIF_NULL_TYPE(str);
698 }
699 return TRUE;
700 }
701
702 static spif_ipsockaddr_t
spif_url_get_ipaddr(spif_url_t self)703 spif_url_get_ipaddr(spif_url_t self)
704 {
705 spif_uint8_t tries;
706 spif_hostinfo_t hinfo;
707 spif_ipsockaddr_t addr;
708 spif_str_t hostname;
709
710 ASSERT_RVAL(!SPIF_URL_ISNULL(self), SPIF_NULL_TYPE(ipsockaddr));
711
712 /* We need a hostname of some type to connect to. */
713 hostname = SPIF_STR(spif_url_get_host(self));
714 REQUIRE_RVAL(!SPIF_STR_ISNULL(hostname), SPIF_NULL_TYPE(ipsockaddr));
715
716 /* Try up to 3 times to resolve the hostname. */
717 h_errno = 0;
718 tries = 0;
719 do {
720 tries++;
721 hinfo = gethostbyname(SPIF_CHARPTR_C(SPIF_STR_STR(hostname)));
722 } while ((tries <= 3) && (hinfo == NULL) && (h_errno == TRY_AGAIN));
723 if (hinfo == NULL) {
724 libast_print_error("Unable to resolve hostname \"%s\" -- %s\n", SPIF_STR_STR(hostname), hstrerror(h_errno));
725 return SPIF_NULL_TYPE(ipsockaddr);
726 }
727
728 if (hinfo->h_addr_list == NULL) {
729 libast_print_error("Invalid address list returned by gethostbyname()\n");
730 return SPIF_NULL_TYPE(ipsockaddr);
731 }
732
733 /* Grab the host IP address and port number, both in network byte order. */
734 addr = SPIF_ALLOC(ipsockaddr);
735 addr->sin_family = AF_INET;
736 addr->sin_port = htons(spif_url_get_portnum(self));
737 memcpy(&(addr->sin_addr), (void *) (hinfo->h_addr_list[0]), sizeof(addr->sin_addr));
738 D_OBJ(("Got address 0x%08x and port %d for name \"%s\"\n", (long) ntohl(*((int *) (&addr->sin_addr))),
739 (int) ntohs(addr->sin_port), SPIF_STR_STR(hostname)));
740 return addr;
741 }
742
743 static spif_unixsockaddr_t
spif_url_get_unixaddr(spif_url_t self)744 spif_url_get_unixaddr(spif_url_t self)
745 {
746 spif_unixsockaddr_t addr;
747
748 ASSERT_RVAL(!SPIF_URL_ISNULL(self), SPIF_NULL_TYPE(unixsockaddr));
749
750 /* No address to look up, just a file path. */
751 addr = SPIF_ALLOC(unixsockaddr);
752 addr->sun_family = AF_UNIX;
753 addr->sun_path[0] = 0;
754 strncat(addr->sun_path, SPIF_CHARPTR_C(SPIF_STR_STR(spif_url_get_path(self))), sizeof(addr->sun_path) - 1);
755 return addr;
756 }
757
758 static spif_sockport_t
spif_url_get_portnum(spif_url_t self)759 spif_url_get_portnum(spif_url_t self)
760 {
761 spif_str_t port_str;
762
763 ASSERT_RVAL(!SPIF_URL_ISNULL(self), SPIF_NULL_TYPE(sockport));
764
765 /* Return the integer form of the port number for a URL */
766 port_str = spif_url_get_port(self);
767 if (!SPIF_STR_ISNULL(port_str)) {
768 return SPIF_CAST(sockport) spif_str_to_num(port_str, 10);
769 }
770
771 return SPIF_CAST(sockport) 0;
772 }
773
774 static spif_bool_t
spif_socket_get_proto(spif_socket_t self)775 spif_socket_get_proto(spif_socket_t self)
776 {
777 spif_url_t url;
778 spif_protoinfo_t proto;
779 spif_str_t proto_str;
780 spif_servinfo_t serv;
781
782 ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
783
784 /* If we have a remote URL, use it. Otherwise, use the local one. */
785 url = ((SPIF_URL_ISNULL(self->remote_url)) ? (self->local_url) : (self->remote_url));
786 REQUIRE_RVAL(!SPIF_URL_ISNULL(url), FALSE);
787
788 proto_str = spif_url_get_proto(url);
789 if (!SPIF_STR_ISNULL(proto_str)) {
790 if (SPIF_CMP_IS_EQUAL(spif_str_cmp_with_ptr(proto_str, SPIF_CHARPTR("raw")))) {
791 spif_str_t target;
792
793 /* Raw socket. Could be raw UNIX or raw IP. */
794 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_TYPE_RAW);
795
796 /* If we have a hostname, it's IP. If we have a path only, it's UNIX. */
797 target = spif_url_get_host(url);
798 if (SPIF_STR_ISNULL(target)) {
799 target = spif_url_get_path(url);
800 if (!SPIF_STR_ISNULL(target)) {
801 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_UNIX);
802 }
803 } else {
804 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_INET);
805 }
806 } else if (SPIF_CMP_IS_EQUAL(spif_str_cmp_with_ptr(proto_str, SPIF_CHARPTR("unix")))) {
807 /* UNIX socket. Assume stream-based. */
808 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_UNIX);
809 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_TYPE_STREAM);
810 } else {
811 /* IP socket. See if they gave us a protocol name. */
812 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_INET);
813 proto = getprotobyname(SPIF_CHARPTR_C(SPIF_STR_STR(proto_str)));
814 if (proto == NULL) {
815 /* If it's not a protocol, it's probably a service. */
816 serv = getservbyname(SPIF_CHARPTR_C(SPIF_STR_STR(proto_str)), "tcp");
817 if (serv == NULL) {
818 serv = getservbyname(SPIF_CHARPTR_C(SPIF_STR_STR(proto_str)), "udp");
819 }
820 if (serv != NULL) {
821 /* If we found one, get the protocol info too. */
822 proto = getprotobyname(serv->s_proto);
823 REQUIRE_RVAL(proto != NULL, FALSE);
824 }
825 }
826 if (proto != NULL) {
827 /* Bingo. Set the flags appropriately. */
828 self->proto = proto->p_proto;
829 if (!strcmp(proto->p_name, "tcp")) {
830 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_TYPE_STREAM);
831 } else if (!strcmp(proto->p_name, "udp")) {
832 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_TYPE_DGRAM);
833 }
834 }
835 }
836 } else {
837 /* No protocol? Assume a UNIX socket. */
838 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_FAMILY_UNIX);
839 SPIF_SOCKET_FLAGS_SET(self, SPIF_SOCKET_FLAGS_TYPE_STREAM);
840 }
841 return TRUE;
842 }
843