1 /* distcache, Distributed Session Caching technology
2 * Copyright (C) 2000-2003 Geoff Thorpe, and Cryptographic Appliances, Inc.
3 * Copyright (C) 2004 The Distcache.org project
4 *
5 * This library is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License as published by the Free
7 * Software Foundation; using version 2.1 of the License. The copyright holders
8 * may elect to allow the application of later versions of the License to this
9 * software, please contact the author (geoff@distcache.org) if you wish us to
10 * review any later version released by the Free Software Foundation.
11 *
12 * This library is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 * details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #define SYS_GENERATING_LIB
23
24 #include <libsys/pre.h>
25 #include <libnal/nal.h>
26 #include "nal_internal.h"
27 #include <libsys/post.h>
28
29 static int int_always_one = 1; /* used in setsockopt() */
30 /* Solaris (among possibly many platforms) doesn't know what SOL_TCP is, we need
31 * to use getprotobyname() to find it. The result is stored here. */
32 static int sol_tcp = -1;
33
34 /*****************************/
35 /* Internal socket functions */
36 /*****************************/
37
int_sockaddr_size(const nal_sockaddr * addr)38 static int int_sockaddr_size(const nal_sockaddr *addr)
39 {
40 switch(addr->type) {
41 case nal_sockaddr_type_ip:
42 return sizeof(struct sockaddr_in);
43 #ifndef WIN32
44 case nal_sockaddr_type_unix:
45 return sizeof(struct sockaddr_un);
46 #endif
47 default:
48 break;
49 }
50 /* for now at least, should *never* happen */
51 abort();
52 /* return 0; */
53 }
54
int_sock_set_reuse(int fd,const nal_sockaddr * addr)55 static int int_sock_set_reuse(int fd, const nal_sockaddr *addr)
56 {
57 int reuseVal = 1;
58 if(addr->type != nal_sockaddr_type_ip)
59 return 1;
60 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
61 (char *)(&reuseVal), sizeof(reuseVal)) != 0)
62 return 0;
63 return 1;
64 }
65
int_sock_bind(int fd,const nal_sockaddr * addr)66 static int int_sock_bind(int fd, const nal_sockaddr *addr)
67 {
68 socklen_t addr_size = int_sockaddr_size(addr);
69 nal_sockaddr tmp;
70
71 if(addr->type == nal_sockaddr_type_unix)
72 /* Stevens' book says do it, so I do. Unfortunately - this
73 * actually needs additional file-locking to prevent one
74 * application stealing another's listener (without him
75 * noticing it's gone even!). */
76 unlink(addr->val.val_un.sun_path);
77 SYS_memcpy(nal_sockaddr, &tmp, addr);
78 if(bind(fd, (struct sockaddr *)&tmp, addr_size) != 0)
79 return 0;
80 return 1;
81 }
82
83 /**********************/
84 /* nal_sock functions */
85 /**********************/
86
nal_sock_set_nagle(int fd,int use_nagle,nal_sockaddr_type type)87 int nal_sock_set_nagle(int fd, int use_nagle, nal_sockaddr_type type)
88 {
89 #ifndef WIN32
90 if(use_nagle || (type != nal_sockaddr_type_ip))
91 return 1;
92
93 if(sol_tcp == -1) {
94 struct protoent *p = getprotobyname("tcp");
95 if(!p) {
96 #if SYS_DEBUG_LEVEL > 1
97 SYS_fprintf(SYS_stderr, "Error, couldn't obtain SOL_TCP\n");
98 #endif
99 return 0;
100 }
101 sol_tcp = p->p_proto;
102 }
103
104 if(setsockopt(fd, sol_tcp, TCP_NODELAY, &int_always_one,
105 sizeof(int_always_one)) != 0) {
106 #if SYS_DEBUG_LEVEL > 1
107 SYS_fprintf(SYS_stderr, "Error, couldn't disable Nagle algorithm\n");
108 #endif
109 return 0;
110 }
111 #endif
112 return 1;
113 }
114
nal_sock_sockaddr_from_ipv4(nal_sockaddr * addr,const char * start_ptr)115 int nal_sock_sockaddr_from_ipv4(nal_sockaddr *addr, const char *start_ptr)
116 {
117 char *tmp_ptr;
118 char *fini_ptr;
119 struct hostent *ip_lookup;
120 /* struct sockaddr_in in_addr; */
121 unsigned long in_ip_piece;
122 unsigned char in_ip[4];
123 int no_ip = 0;
124
125 addr->caps = 0;
126 /* We're an IPv4 address, and start_ptr points to the first character
127 * of the address part */
128 if(strlen(start_ptr) < 1) return 0;
129 /* Logic: if our string contains another ":" we assume it's of the form
130 * nnn.nnn.nnn.nnn:nnn, otherwise assume IP[v4]:nnn. Exception,
131 * if it's of the form IP[v4]::nnn, we treat it as equivalent to one
132 * colon. */
133 if(((fini_ptr = strstr(start_ptr, ":")) == NULL) ||
134 (start_ptr == fini_ptr)) {
135 /* No colon, skip the IP address - this is listen-only */
136 no_ip = 1;
137 /* If it's a double colon, we need to increment start_ptr */
138 if(fini_ptr)
139 start_ptr++;
140 goto ipv4_port;
141 }
142 /* Create a temporary string for the isolated hostname/ip-address */
143 tmp_ptr = SYS_malloc(char, (int)(fini_ptr - start_ptr) + 1);
144 if(!tmp_ptr)
145 return 0;
146 SYS_memcpy_n(char, tmp_ptr, start_ptr,
147 (int)(fini_ptr - start_ptr));
148 tmp_ptr[(int)(fini_ptr - start_ptr)] = '\0';
149 ip_lookup = gethostbyname(tmp_ptr);
150 SYS_free(char, tmp_ptr);
151 if(!ip_lookup)
152 /* Host not understood or recognised */
153 return 0;
154 /* Grab the IP address and move on (h_addr_list[0] is signed char?!) */
155 SYS_memcpy_n(char, (char *)in_ip, ip_lookup->h_addr_list[0], 4);
156 /* Align start_ptr to the start of the "port" number. */
157 start_ptr = fini_ptr + 1;
158 /* Ok, this is an address that could be used for connecting */
159 addr->caps |= NAL_ADDRESS_CAN_CONNECT;
160
161 ipv4_port:
162 if(strlen(start_ptr) < 1)
163 return 0;
164 /* start_ptr points to the first character of the port part */
165 in_ip_piece = strtoul(start_ptr, &fini_ptr, 10);
166 if((in_ip_piece > 65535) || (*fini_ptr != '\0'))
167 return 0;
168 /* populate the sockaddr_in structure */
169 addr->val.val_in.sin_family = AF_INET;
170 if(no_ip)
171 addr->val.val_in.sin_addr.s_addr = INADDR_ANY;
172 else
173 SYS_memcpy_n(unsigned char,
174 (unsigned char *)&addr->val.val_in.sin_addr.s_addr, in_ip, 4);
175 addr->val.val_in.sin_port = htons((unsigned short)in_ip_piece);
176 /* ipv4 addresses are always good for listening */
177 addr->caps |= NAL_ADDRESS_CAN_LISTEN;
178 addr->type = nal_sockaddr_type_ip;
179 return 1;
180 }
181
182 #ifndef WIN32
nal_sock_sockaddr_from_unix(nal_sockaddr * addr,const char * start_ptr)183 int nal_sock_sockaddr_from_unix(nal_sockaddr *addr, const char *start_ptr)
184 {
185 struct sockaddr_un un_addr;
186
187 un_addr.sun_family = AF_UNIX;
188 SYS_strncpy(un_addr.sun_path, start_ptr, UNIX_PATH_MAX);
189 /* Now sandblast the sockaddr_un structure onto the sockaddr structure
190 * (which one hopes is greater than or equal to it in size :-). */
191 SYS_zero(nal_sockaddr, addr);
192 SYS_memcpy(struct sockaddr_un, &addr->val.val_un, &un_addr);
193 addr->type = nal_sockaddr_type_unix;
194 addr->caps = NAL_ADDRESS_CAN_LISTEN | NAL_ADDRESS_CAN_CONNECT;
195 return 1;
196 }
197 #endif
198
nal_sock_create_socket(int * fd,const nal_sockaddr * addr)199 int nal_sock_create_socket(int *fd, const nal_sockaddr *addr)
200 {
201 int tmp_fd = -1;
202 switch(addr->type) {
203 case nal_sockaddr_type_ip:
204 tmp_fd = socket(PF_INET, SOCK_STREAM, 0);
205 break;
206 #ifndef WIN32
207 case nal_sockaddr_type_unix:
208 tmp_fd = socket(PF_UNIX, SOCK_STREAM, 0);
209 break;
210 #endif
211 default:
212 /* Should never happen */
213 abort();
214 }
215 if(tmp_fd < 0) {
216 #if SYS_DEBUG_LEVEL > 1
217 SYS_fprintf(SYS_stderr, "Error, can't create socket\n\n");
218 #endif
219 return 0;
220 }
221 *fd = tmp_fd;
222 return 1;
223 }
224
225 #ifndef WIN32
nal_sock_create_unix_pair(int sv[2])226 int nal_sock_create_unix_pair(int sv[2])
227 {
228 if(socketpair(PF_UNIX, SOCK_STREAM, 0, sv) != 0) {
229 #if SYS_DEBUG_LEVEL > 1
230 SYS_fprintf(SYS_stderr, "Error, can't create socketpair\n\n");
231 #endif
232 return 0;
233 }
234 return 1;
235 }
236 #endif
237
nal_sock_connect(int fd,const nal_sockaddr * addr,int * established)238 int nal_sock_connect(int fd, const nal_sockaddr *addr,
239 int *established)
240 {
241 socklen_t addr_size = int_sockaddr_size(addr);
242 nal_sockaddr tmp;
243
244 SYS_memcpy(nal_sockaddr, &tmp, addr);
245 if(connect(fd, (struct sockaddr *)&tmp, addr_size) != 0) {
246 #ifdef WIN32
247 if(WSAGetLastError() != WSAEWOULDBLOCK)
248 #else
249 if(errno != EINPROGRESS)
250 #endif
251 {
252 #if SYS_DEBUG_LEVEL > 1
253 SYS_fprintf(SYS_stderr, "Error, couldn't connect\n\n");
254 #endif
255 return 0;
256 }
257 /* non-blocking connect ... connect() succeeded, but it may yet
258 * fail without a single byte going anywhere. */
259 *established = 0;
260 } else
261 *established = 1;
262 return 1;
263 }
264
nal_sock_listen(int fd,const nal_sockaddr * addr)265 int nal_sock_listen(int fd, const nal_sockaddr *addr)
266 {
267 if(!int_sock_set_reuse(fd, addr) || !int_sock_bind(fd, addr))
268 return 0;
269 if(listen(fd, NAL_LISTENER_BACKLOG) != 0)
270 return 0;
271 return 1;
272 }
273
nal_sock_accept(int listen_fd,int * conn)274 int nal_sock_accept(int listen_fd, int *conn)
275 {
276 if((*conn = accept(listen_fd, NULL, NULL)) == -1) {
277 #if SYS_DEBUG_LEVEL > 1
278 SYS_fprintf(SYS_stderr, "Error, accept failed\n\n");
279 #endif
280 return 0;
281 }
282 return 1;
283 }
284
nal_sock_is_connected(int fd)285 int nal_sock_is_connected(int fd)
286 {
287 int t;
288 socklen_t t_len = sizeof(t);
289 /* the ugly cast is necessary with my system headers to avoid warnings,
290 * but there's probably a reason and/or autoconf things to do with
291 * this. */
292 if((getsockopt(fd, SOL_SOCKET, SO_ERROR, &t,
293 (unsigned int *)&t_len) != 0) || (t != 0))
294 return 0;
295 return 1;
296 }
297
298 /* To save code-duplication, work out requirements once */
299 #if !defined(WIN32) && defined(HAVE_GETSOCKNAME) && defined(HAVE_STRTOUL) && \
300 defined(HAVE_CHOWN) && defined(HAVE_CHMOD) && defined(HAVE_GETPWNAM)
301 #define NAL_SOCKADDR_UNIX
302 #endif
303
nal_sockaddr_get(nal_sockaddr * addr,int fd)304 int nal_sockaddr_get(nal_sockaddr *addr, int fd)
305 {
306 #ifdef NAL_SOCKADDR_UNIX
307 socklen_t pathlen = sizeof(addr->val.val_un);
308 /* I get a "pointer target in arg 3 differ in signedness" warning from
309 * gcc despite the fact I am following the exact prototype! So the
310 * (void*) cast is just to avoid false positives from -Werror. */
311 if(getsockname(fd, (struct sockaddr *)&addr->val.val_un,
312 (void*)&pathlen) != 0)
313 return 0;
314 addr->type = nal_sockaddr_type_unix;
315 addr->caps = 0; /* Can't listen or connect */
316 return 1;
317 #else
318 return 0;
319 #endif
320 }
321
nal_sockaddr_chown(const nal_sockaddr * addr,const char * username,const char * groupname)322 int nal_sockaddr_chown(const nal_sockaddr *addr, const char *username,
323 const char *groupname)
324 {
325 #ifdef NAL_SOCKADDR_UNIX
326 /* according to chown(2), -1 can be used as an owner or group value to
327 * specify "no change". */
328 struct passwd *p = (username ? getpwnam(username) : NULL);
329 uid_t uid = (p ? p->pw_uid : (uid_t)-1);
330 gid_t gid = (p ? p->pw_gid : (uid_t)-1);
331 #if defined(HAVE_GETGRNAM)
332 struct group *g = (groupname ? getgrnam(groupname) : NULL);
333 /* Err if 'groupname' is invalid */
334 if(groupname && !g) return 0;
335 if(g) gid = g->gr_gid;
336 #endif
337 /* Err if 'username' is invalid */
338 if(username && !p) return 0;
339 /* Err if 'addr' is not unix */
340 if(addr->type != nal_sockaddr_type_unix) return 0;
341 if(chown(addr->val.val_un.sun_path, uid, gid) != 0) return 0;
342 return 1;
343 #else
344 return 0;
345 #endif
346 }
nal_sockaddr_chmod(const nal_sockaddr * addr,const char * octal_string)347 int nal_sockaddr_chmod(const nal_sockaddr *addr, const char *octal_string)
348 {
349 #ifdef NAL_SOCKADDR_UNIX
350 unsigned long n;
351 char *endptr;
352 /* Err if 'addr' is not unix */
353 if(addr->type != nal_sockaddr_type_unix) return 0;
354 /* Parse the octal */
355 n = strtol(octal_string, &endptr, 8);
356 if ((endptr == octal_string) || (*endptr != '\0') || (n == ULONG_MAX))
357 return 0;
358 if (chmod(addr->val.val_un.sun_path, n) != 0) return 0;
359 return 1;
360 #else
361 return 0;
362 #endif
363 }
364