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