2 * libauthbind.c - bind(2)-redirector library for authbind
4 * authbind is Copyright (C) 1998 Ian Jackson
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <netinet/in.h>
36 #include <sys/socket.h>
38 #include <arpa/inet.h>
40 #define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1)
41 #define STDERRSTR_STRING(m) write(2,m,strlen(m))
43 typedef void anyfn_type(void);
45 static anyfn_type *find_any(const char *name) {
46 static const char *dlerr;
49 kv= dlsym(RTLD_NEXT,name); if (kv) return kv;
50 dlerr= dlerror(); if (!dlerr) dlerr= "dlsym() failed for no reason";
51 STDERRSTR_CONST("libauthbind: error finding original version of ");
52 STDERRSTR_STRING(name);
53 STDERRSTR_CONST(": ");
54 STDERRSTR_STRING(dlerr);
55 STDERRSTR_STRING("\n");
60 #define socket_args int domain, int type, int protocol
61 #define close_args int fd
62 #define bind_args int fd, const struct sockaddr *addr, socklen_t addrlen
63 #define setsockopt_args int fd, int level, int optname, \
64 const void *optval, socklen_t optlen
65 #define getsockname_args int fd, struct sockaddr *addr, socklen_t *addrlen
67 X(socket, (domain,type,protocol)) \
69 X(bind, (fd,addr,addrlen)) \
70 X(setsockopt, (fd,level,optname,optval,optlen)) \
71 X(getsockname,(fd,addr,addrlen))
73 #define DEF_OLD(fn,args) \
74 typedef int fn##_fn_type(fn##_args); \
75 static int find_##fn(fn##_args); \
76 static fn##_fn_type find_##fn, *old_##fn=find_##fn; \
77 static int find_##fn(fn##_args) { \
79 anyfn= find_any(#fn); if (!anyfn) return -1; \
80 old_##fn= (fn##_fn_type*)anyfn; \
81 return old_##fn args; \
86 #define WRAP(fn) int fn(fn##_args)
91 static fdinfo **table;
94 static fdinfo *lookup(int fd) {
95 if (fd>=tablesz) return 0;
99 #define ADDRPORTSTRLEN (INET6_ADDRSTRLEN+1+5) /* not including nul */
101 static int addrport2str(char buf[ADDRPORTSTRLEN+1],
102 const struct sockaddr *addr, socklen_t addrlen) {
103 const void *addrv=addr;
105 const struct sockaddr_in *sin;
106 const struct sockaddr_in6 *sin6;
109 switch (addr->sa_family) {
110 case AF_INET: sin =addrv; el=sizeof(*sin ); iav=&sin ->sin_addr ; port=sin ->sin_port ; break;
111 case AF_INET6: sin6=addrv; el=sizeof(*sin6); iav=&sin6->sin6_addr; port=sin6->sin6_port; break;
112 default: errno=ESRCH; return -1;
114 //fprintf(stderr,"af=%lu el=%lu addrlen=%lu\n",
115 // (unsigned long)addr->sa_family,
116 // (unsigned long)el,
117 // (unsigned long)addrlen);
118 if (addrlen!=el) { errno=EINVAL; return -1; }
120 if (!inet_ntop(addr->sa_family,iav,p,INET6_ADDRSTRLEN)) return -1;
122 sprintf(p,",%u",(unsigned)ntohs(port));
126 static int str2addrport(char *str,
127 struct sockaddr *addr, socklen_t *addrlen) {
129 struct sockaddr_in sin;
130 struct sockaddr_in6 sin6;
133 memset(&si,0,sizeof(si));
139 switch (str[strcspn(str,".:")]) {
140 case '.': af=AF_INET ; iav=&si.sin .sin_addr ; al=sizeof(si.sin ); portp=&si.sin .sin_port ; break;
141 case ':': af=AF_INET6; iav=&si.sin6.sin6_addr; al=sizeof(si.sin6); portp=&si.sin6.sin6_port; break;
142 default: errno=ESRCH; return -1;
144 si.sin.sin_family=af;
146 char *comma=strchr(str,',');
147 if (!comma) { errno=ESRCH; return -1; }
149 if (inet_pton(af,str,iav)) return -1;
153 unsigned long port=strtoul(comma,&ep,10);
154 if (ep==comma || *ep || errno || port>65536) { errno=ESRCH; return -1; }
157 if (addr) memcpy(addr,&si, *addrlen<al ? *addrlen : al);
163 if (!((domain==AF_INET || domain==AF_INET6) &&
165 return old_socket(domain,type,protocol);
166 int fd=socket(AF_UNIX,SOCK_DGRAM,0);
170 table=realloc(table,newsz*sizeof(*table));
171 if (!table) goto fail;
172 while (tablesz<newsz) table[tablesz++]=0;
175 table[fd]=malloc(sizeof(*table[fd]));
176 if (!table[fd]) goto fail;
177 table[fd]->af=domain;
190 return old_close(fd);
194 fdinfo *ent=lookup(fd);
195 if (!ent) return old_bind(fd,addr,addrlen);
196 const char *dir = getenv("UDP_PRELOAD_DIR");
197 if (!dir) { errno=ECHILD; return -1; }
198 struct sockaddr_un sun;
199 memset(&sun,0,sizeof(sun));
200 sun.sun_family=AF_UNIX;
201 int dl = strlen(dir);
202 if (dl + 1 + ADDRPORTSTRLEN + 1 > sizeof(sun.sun_path)) {
203 errno=ENAMETOOLONG; return -1;
205 strcpy(sun.sun_path,dir);
206 char *p=sun.sun_path+dl;
208 if (addrport2str(p,addr,addrlen)) return -1;
209 //fprintf(stderr,"binding %s\n",sun.sun_path);
210 if (unlink(sun.sun_path) && errno!=ENOENT) return -1;
211 return old_bind(fd,(const void*)&sun,sizeof(sun));
215 fdinfo *ent=lookup(fd);
216 if (!ent) return old_setsockopt(fd,level,optname,optval,optlen);
217 if (ent->af==AF_INET6 && level==IPPROTO_IPV6 && optname==IPV6_V6ONLY
218 && optlen==sizeof(int) && *(int*)optval==1) {
226 fdinfo *ent=lookup(fd);
227 if (!ent) return old_getsockname(fd,addr,addrlen);
228 struct sockaddr_un sun;
229 socklen_t sunlen=sizeof(sun);
230 if (old_getsockname(fd,(void*)&sun,&sunlen)) return -1;
231 if (sun.sun_family!=AF_UNIX || sunlen>sizeof(sun)) {
232 //fprintf(stderr,"old_getsockname af=%lu sunlen=%lu\n",
233 // (unsigned long)sun.sun_family,
234 // (unsigned long)sunlen);
235 errno=EDOM; return -1;
237 if (str2addrport(sun.sun_path,addr,addrlen)) return -1;