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 bind_args int fd, const struct sockaddr *addr, socklen_t addrlen
62 #define setsockopt_args int fd, int level, int optname, \
63 const void *optval, socklen_t optlen
64 #define getsockname_args int fd, struct sockaddr *addr, socklen_t *addrlen
66 X(socket, (domain,type,protocol)) \
67 X(bind, (fd,addr,addrlen)) \
68 X(setsockopt, (fd,level,optname,optval,optlen)) \
69 X(getsockname,(fd,addr,addrlen))
71 #define DEF_OLD(fn,args) \
72 typedef int fn##_fn_type(fn##_args); \
73 static int find_##fn(fn##_args); \
74 static fn##_fn_type find_##fn, *old_##fn=find_##fn; \
75 static int find_##fn(fn##_args) { \
77 anyfn= find_any(#fn); if (!anyfn) return -1; \
78 old_##fn= (fn##_fn_type*)anyfn; \
79 return old_##fn args; \
84 #define WRAP(fn) int fn(fn##_args)
89 static fdinfo **table;
92 static fdinfo *lookup(int fd) {
93 if (fd>=tablesz) return 0;
97 #define ADDRPORTSTRLEN (INET6_ADDRSTRLEN+1+5) /* not including nul */
99 static int addrport2str(char buf[ADDRPORTSTRLEN+1],
100 const struct sockaddr *addr, socklen_t addrlen) {
101 const void *addrv=addr;
103 const struct sockaddr_in *sin;
104 const struct sockaddr_in6 *sin6;
107 switch (addr->sa_family) {
108 case AF_INET: sin =addrv; el=sizeof(*sin ); iav=&sin ->sin_addr ; port=sin ->sin_port ; break;
109 case AF_INET6: sin6=addrv; el=sizeof(*sin6); iav=&sin6->sin6_addr; port=sin6->sin6_port; break;
110 default: errno=ESRCH; return -1;
112 //fprintf(stderr,"af=%lu el=%lu addrlen=%lu\n",
113 // (unsigned long)addr->sa_family,
114 // (unsigned long)el,
115 // (unsigned long)addrlen);
116 if (addrlen!=el) { errno=EINVAL; return -1; }
118 if (!inet_ntop(addr->sa_family,iav,p,INET6_ADDRSTRLEN)) return -1;
120 sprintf(p,",%u",(unsigned)ntohs(port));
124 static int str2addrport(char *str,
125 struct sockaddr *addr, socklen_t *addrlen) {
127 struct sockaddr_in sin;
128 struct sockaddr_in6 sin6;
131 memset(&si,0,sizeof(si));
137 switch (str[strcspn(str,".:")]) {
138 case '.': af=AF_INET ; iav=&si.sin .sin_addr ; al=sizeof(si.sin ); portp=&si.sin .sin_port ; break;
139 case ':': af=AF_INET6; iav=&si.sin6.sin6_addr; al=sizeof(si.sin6); portp=&si.sin6.sin6_port; break;
140 default: errno=ESRCH; return -1;
142 si.sin.sin_family=af;
144 char *comma=strchr(str,',');
145 if (!comma) { errno=ESRCH; return -1; }
147 if (inet_pton(af,str,iav)) return -1;
151 unsigned long port=strtoul(comma,&ep,10);
152 if (ep==comma || *ep || errno || port>65536) { errno=ESRCH; return -1; }
155 if (addr) memcpy(addr,&si, *addrlen<al ? *addrlen : al);
161 if (!((domain==AF_INET || domain==AF_INET6) &&
163 return old_socket(domain,type,protocol);
164 int fd=socket(AF_UNIX,SOCK_DGRAM,0);
168 table=realloc(table,newsz*sizeof(*table));
169 if (!table) goto fail;
170 while (tablesz<newsz) table[tablesz++]=0;
173 table[fd]=malloc(sizeof(*table[fd]));
174 if (!table[fd]) goto fail;
175 table[fd]->af=domain;
184 fdinfo *ent=lookup(fd);
185 if (!ent) return old_bind(fd,addr,addrlen);
186 const char *dir = getenv("UDP_PRELOAD_DIR");
187 if (!dir) { errno=ECHILD; return -1; }
188 struct sockaddr_un sun;
189 memset(&sun,0,sizeof(sun));
190 sun.sun_family=AF_UNIX;
191 int dl = strlen(dir);
192 if (dl + 1 + ADDRPORTSTRLEN + 1 > sizeof(sun.sun_path)) {
193 errno=ENAMETOOLONG; return -1;
195 strcpy(sun.sun_path,dir);
196 char *p=sun.sun_path+dl;
198 if (addrport2str(p,addr,addrlen)) return -1;
199 //fprintf(stderr,"binding %s\n",sun.sun_path);
200 if (unlink(sun.sun_path) && errno!=ENOENT) return -1;
201 return old_bind(fd,(const void*)&sun,sizeof(sun));
205 fdinfo *ent=lookup(fd);
206 if (!ent) return old_setsockopt(fd,level,optname,optval,optlen);
207 if (ent->af==AF_INET6 && level==IPPROTO_IPV6 && optname==IPV6_V6ONLY
208 && optlen==sizeof(int) && *(int*)optval==1) {
216 fdinfo *ent=lookup(fd);
217 if (!ent) return old_getsockname(fd,addr,addrlen);
218 struct sockaddr_un sun;
219 socklen_t sunlen=sizeof(sun);
220 if (old_getsockname(fd,(void*)&sun,&sunlen)) return -1;
221 if (sun.sun_family!=AF_UNIX || sunlen>sizeof(sun)) {
222 //fprintf(stderr,"old_getsockname af=%lu sunlen=%lu\n",
223 // (unsigned long)sun.sun_family,
224 // (unsigned long)sunlen);
225 errno=EDOM; return -1;
227 if (str2addrport(sun.sun_path,addr,addrlen)) return -1;