added gentoo ebuilds
[libmixp.git] / libmixp / socket.c
blob9f5e65c50c88364b7e2aaa8885c8e6dcf5efb382
1 /* (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
2 * Copyright ©2007 Kris Maglione <fbsdaemon@gmail.com>
3 * See LICENSE file for license details.
4 */
5 #include <errno.h>
6 #include <netdb.h>
7 #include <netinet/in.h>
8 #include <signal.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <sys/stat.h>
13 #include <sys/socket.h>
14 #include <sys/un.h>
15 #include <unistd.h>
16 #include "mixp_local.h"
17 #include <9p-mixp/srv_addr.h>
18 #include <9p-mixp/err.h>
20 /* Note: These functions modify the strings that they are passed.
21 * The lookup function duplicates the original string, so it is
22 * not modified.
25 typedef struct sockaddr sockaddr;
26 typedef struct sockaddr_un sockaddr_un;
27 typedef struct sockaddr_in sockaddr_in;
29 static int
30 get_port(const char *addr) {
31 char *s, *end;
32 int port;
34 s = strchr(addr, '!');
35 if(s == NULL) {
36 mixp_werrstr("no port provided");
37 return -1;
40 *s++ = '\0';
41 port = strtol(s, &end, 10);
42 if(*s == '\0' && *end != '\0') {
43 mixp_werrstr("invalid port number");
44 return -1;
46 return port;
49 static int
50 sock_unix(const char *address, sockaddr_un *sa, socklen_t *salen) {
51 int fd;
53 memset(sa, 0, sizeof(sa));
55 sa->sun_family = AF_UNIX;
56 strncpy(sa->sun_path, address, sizeof(sa->sun_path));
57 *salen = SUN_LEN(sa);
59 fd = socket(AF_UNIX, SOCK_STREAM, 0);
60 if(fd < 0)
61 return -1;
62 return fd;
65 static int
66 sock_tcp_2(const char *host, int port, sockaddr_in *sa) {
67 struct hostent *he;
68 int fd;
70 if(port < 0)
71 return -1;
73 signal(SIGPIPE, SIG_IGN);
74 fd = socket(AF_INET, SOCK_STREAM, 0);
75 if(fd < 0)
76 return -1;
78 memset(sa, 0, sizeof(sa));
79 sa->sin_family = AF_INET;
80 sa->sin_port = htons(port);
82 if(strcmp(host, "*") == 0)
83 sa->sin_addr.s_addr = htonl(INADDR_ANY);
84 else if((he = gethostbyname(host)))
85 memcpy(&sa->sin_addr, he->h_addr, he->h_length);
86 else
87 return -1;
88 return fd;
91 static int
92 dial_unix(const char *address) {
93 sockaddr_un sa;
94 socklen_t salen;
95 int fd;
97 fd = sock_unix(address, &sa, &salen);
98 if(fd == -1)
99 return fd;
101 if(connect(fd, (sockaddr*) &sa, salen)) {
102 close(fd);
103 return -1;
105 return fd;
108 static int
109 announce_unix(const char *file) {
110 const int yes = 1;
111 sockaddr_un sa;
112 socklen_t salen;
113 int fd;
115 signal(SIGPIPE, SIG_IGN);
117 fd = sock_unix(file, &sa, &salen);
118 if(fd == -1)
119 return fd;
121 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes)) < 0)
122 goto fail;
124 unlink(file);
125 if(bind(fd, (sockaddr*)&sa, salen) < 0)
126 goto fail;
128 chmod(file, S_IRWXU);
129 if(listen(fd, IXP_MAX_CACHE) < 0)
130 goto fail;
132 return fd;
134 fail:
135 close(fd);
136 return -1;
139 static int dial_tcp_2(const char *host, int port)
141 sockaddr_in sa;
142 int fd;
144 fd = sock_tcp_2(host, port, &sa);
145 if(fd == -1)
146 return fd;
148 if(connect(fd, (sockaddr*)&sa, sizeof(sa))) {
149 close(fd);
150 return -1;
153 return fd;
156 static int dial_tcp(const char *host)
158 int port = get_port(host);
159 return dial_tcp_2(host,port);
162 static int
163 announce_tcp(const char *host) {
164 sockaddr_in sa;
165 int fd;
166 int port;
168 port = get_port(host);
170 fd = sock_tcp_2(host, port, &sa);
171 if(fd == -1)
172 return fd;
174 if(bind(fd, (sockaddr*)&sa, sizeof(sa)) < 0)
175 goto fail;
177 if(listen(fd, IXP_MAX_CACHE) < 0)
178 goto fail;
180 return fd;
182 fail:
183 close(fd);
184 return -1;
187 typedef struct addrtab addrtab;
188 struct addrtab {
189 char *type;
190 int (*fn)(const char*);
191 } dtab[] = {
192 {"tcp", dial_tcp},
193 {"unix", dial_unix},
194 {0, 0}
195 }, atab[] = {
196 {"tcp", announce_tcp},
197 {"unix", announce_unix},
198 {0, 0}
201 static int
202 lookup(const char *address, addrtab *tab) {
203 char *addr, *type;
204 int ret;
206 ret = -1;
207 type = strdup(address);
209 addr = strchr(type, '!');
210 if(addr == NULL)
211 mixp_werrstr("no address type defined");
212 else {
213 *addr++ = '\0';
214 for(; tab->type; tab++)
215 if(strcmp(tab->type, type) == 0) break;
216 if(tab->type == NULL)
217 mixp_werrstr("unsupported address type");
218 else
219 ret = tab->fn(addr);
222 free(type);
223 return ret;
226 int mixp_dial_addr(MIXP_SERVER_ADDRESS *addr)
228 switch (addr->proto)
230 case P9_PROTO_TCP:
231 return dial_tcp_2(addr->hostname, addr->port);
232 case P9_PROTO_UNIX:
233 return dial_unix(addr->path);
234 default:
235 return dial_tcp_2(addr->hostname, addr->port);
239 int mixp_dial(const char *address)
241 int ret;
242 MIXP_SERVER_ADDRESS* addr = mixp_srv_addr_parse(address);
243 if (addr==NULL)
244 return -1;
245 ret = mixp_dial_addr(addr);
246 mixp_srv_addr_free(addr);
247 return ret;
250 int mixp_announce(const char *address)
252 return lookup(address, atab);