2005-11-11 Carl Worth <cworth@cworth.org>
[ttt] / src / ttt-socket.c
1 /* ttt-socket.c - Simple interface for creating sockets.
2  *
3  * Copyright © 2005 Carl Worth
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Author: Carl Worth <cworth@cworth.org>
20  */
21
22 #include "ttt-socket.h"
23
24 #include <netinet/in.h>
25 #include <netdb.h>
26
27 /* Initialize a TCP/IP address structure with the given host and port.
28  *
29  * Lookup for host (e.g. /etc/hosts and DNA) and port (e.g /etc/services)
30  * will be performed if necessary.
31  *
32  * A special host value of "0.0.0.0" may be used to prepare a server
33  * socket to bind to all available addresses.
34  *
35  * Errors: If any lookup error occurs, this function will not return.
36  */
37 static void
38 _sockaddr_init (struct sockaddr_in      *addr,
39                 const char              *host,
40                 const char              *port)
41 {
42     struct hostent *hostent;
43     struct servent *servent;
44     char *endptr;
45     long port_long;
46     uint16_t port_short;
47
48     addr->sin_family = AF_INET;
49
50     hostent = gethostbyname (host);
51     if (hostent == NULL)
52     {
53         fprintf (stderr, "Error: Lookup failed for host %s: %s\n",
54                  host, hstrerror (h_errno));
55         exit (1);
56     }
57     memcpy (&addr->sin_addr.s_addr, hostent->h_addr_list[0], 
58             sizeof (addr->sin_addr.s_addr));
59
60     assert (*port != '\0');
61     port_long = strtol (port, &endptr, 10);
62     if (*endptr == '\0')
63     {
64         /* Numeric port*/
65         if (port_long <= 0 || port_long >= (1 << 16)) {
66             fprintf (stderr, "Error: Port %ld out of range.\n", port_long);
67             exit (1);
68         }
69         port_short = port_long;
70         addr->sin_port = htons (port_short);
71     } else {
72         /* Named port */
73         servent = getservbyname (port, "tcp");
74         if (port == NULL) {
75             fprintf (stderr, "Error: Lookup failed for port %s: %s\n",
76                      port, hstrerror (h_errno));
77             exit (1);
78         }
79         addr->sin_port = servent->s_port;
80     }
81 }
82
83 static int
84 _wait_for_connection (int listen_socket)
85 {
86     fd_set read_set;
87     int num_selected;
88     int flags, err;
89     int connected_socket;
90
91     FD_ZERO (&read_set);
92     FD_SET (listen_socket, &read_set);
93
94     while (1) {
95         num_selected = select (listen_socket + 1,
96                                &read_set, NULL, NULL,
97                                NULL);
98
99         flags = fcntl (listen_socket, F_GETFL);
100         flags |= O_NONBLOCK;
101         xfcntl (listen_socket, F_SETFL, flags);
102
103         connected_socket = accept (listen_socket, 0, 0);
104         err = errno;
105
106         flags &= ~O_NONBLOCK;
107         xfcntl (listen_socket, F_SETFL, flags);
108
109         if (connected_socket != -1)
110             return connected_socket;
111
112         if (err == EWOULDBLOCK || err == EAGAIN)
113             continue;
114
115         fprintf (stderr, "Error: accept failed: %s. Aborting.\n",
116                  strerror (err));
117         exit (1);
118     }
119 }
120
121 /* Exported: see ttt-socket.h for documentation. */
122 int
123 ttt_socket_create_server (const char *host, const char *port)
124 {
125     int listen_socket;
126     struct sockaddr_in addr;
127
128     listen_socket = xsocket (PF_INET, SOCK_STREAM, 0);
129
130     _sockaddr_init (&addr, host, port);
131
132 #ifdef SO_REUSEADDR
133     {
134         int one = 1;
135         setsockopt (listen_socket, SOL_SOCKET,
136                     SO_REUSEADDR, (char *) &one, sizeof (int));
137     }
138 #endif
139
140     xbind (listen_socket, (struct sockaddr *) &addr, sizeof (addr));
141
142     xlisten (listen_socket, SOMAXCONN);
143
144     return listen_socket;
145 }
146
147 /* Exported: see ttt-socket.h for documentation. */
148 void
149 ttt_socket_accept (int listen_socket, ttt_socket_dispatch_func_t dispatch)
150 {
151     int connected_socket;
152
153     connected_socket = _wait_for_connection (listen_socket);
154
155     (dispatch) (connected_socket);
156 }