]> git.cworth.org Git - ttt/blob - src/ttt-socket.c
20b96e263056103b87752fe6da432b5c8a89b63b
[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,
150                    ttt_socket_accept_func_t      accept,
151                    void                         *closure)
152 {
153     int connected_socket;
154
155     connected_socket = _wait_for_connection (listen_socket);
156
157     (accept) (closure, connected_socket);
158 }
159
160 /* Exported: see wdo-socket.h for documentation. */
161 ttt_status_t
162 ttt_socket_create_client (const char    *host,
163                           const char    *port,
164                           int           *socket_ret)
165 {
166     ttt_status_t status;
167     struct sockaddr_in addr;
168     int socket;
169
170     *socket_ret = -1;
171
172     socket = xsocket (PF_INET, SOCK_STREAM, 0);
173
174     _sockaddr_init (&addr, host, port);
175
176     status = xconnect (socket, (struct sockaddr *) &addr, sizeof (addr));
177     if (status) {
178         shutdown (socket, SHUT_RDWR);
179         return status;
180     }
181
182     *socket_ret = socket;
183     return TTT_STATUS_SUCCESS;
184 }
185
186 /* Exported: see ttt-socket.h for documentation. */
187 void
188 ttt_socket_read (int     socket,
189                  void   *buf,
190                  size_t  count)
191 {
192     int bytes_read;
193     char *cbuf = buf;
194
195     while (count) {
196         bytes_read = xread (socket, cbuf, count);
197         cbuf += bytes_read;
198         count -= bytes_read;
199     }
200 }
201
202 /* Exported: see ttt-socket.h for documentation. */
203 void
204 ttt_socket_write (int            socket,
205                   const void    *buf,
206                   size_t         count)
207 {
208     int written;
209     const char *cbuf = buf;
210
211     while (count) {
212         written = xwrite (socket, cbuf, count);
213         cbuf += written;
214         count -= written;
215     }
216 }