Per user standard switch (~/.vde2/stdsock: it can be a symlink to the actual switch)
[vde.git] / vde-2 / src / lib / libvdeplug.c
blob8de790c25e3b52af96474fa8b3d7d0161214f082
1 /*
2 * libvdeplug - A library to connect to a VDE Switch.
3 * Copyright (C) 2006 Renzo Davoli, University of Bologna
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation version 2.1 of the License, or (at
8 * your option) any later version.
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <errno.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <pwd.h>
28 #include <grp.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <string.h>
34 #include <config.h>
35 #include <vde.h>
36 #include <vdecommon.h>
38 #include <libvdeplug.h>
40 /* Per-User standard switch definition */
41 /* This will be prefixed by getenv("HOME") */
42 /* it can be a symbolic link to the switch dir */
43 #define STDSOCK "/.vde2/stdsock"
45 #ifdef USE_IPN
46 #ifndef AF_IPN
47 #define AF_IPN 34 /* IPN sockets */
48 #define PF_IPN AF_IPN
49 #endif
50 #ifndef AF_NETBEUI
51 #ifdef PF_NETBEUI
52 #define AF_NETBEUI PF_NETBEUI
53 #else
54 #define AF_NETBEUI 13
55 #endif
56 #endif
57 #define AF_IPN_STOLEN AF_NETBEUI /* IPN temporary sockets */
58 #define PF_IPN_STOLEN AF_IPN_STOLEN
59 #define IPN_ANY 0
61 #define IPN_SO_PORT 0
62 #define IPN_SO_DESCR 1
63 #endif
65 /* Fallback names for the control socket, NULL-terminated array of absolute
66 * filenames. */
67 char *fallback_sockname[] = {
68 "/var/run/vde.ctl/ctl",
69 "/tmp/vde.ctl/ctl",
70 "/tmp/vde.ctl",
71 NULL,
74 /* Fallback directories for the data socket, NULL-terminated array of absolute
75 * directory names, with no trailing /. */
76 const char *fallback_dirname[] = {
77 "/var/run",
78 "/var/tmp",
79 "/tmp",
80 NULL,
83 struct vdeconn {
84 int fdctl;
85 int fddata;
86 struct sockaddr_un inpath;
89 #define SWITCH_MAGIC 0xfeedface
90 #define MAXDESCR 128
92 enum request_type { REQ_NEW_CONTROL, REQ_NEW_PORT0 };
94 struct request_v3 {
95 uint32_t magic;
96 uint32_t version;
97 enum request_type type;
98 struct sockaddr_un sock;
99 char description[MAXDESCR];
100 } __attribute__((packed));
102 VDECONN *vde_open_real(char *given_sockname, char *descr,int interface_version,
103 struct vde_open_args *open_args)
105 struct vdeconn *conn;
106 struct passwd *callerpwd;
107 struct request_v3 req;
108 int pid = getpid();
109 static struct sockaddr_un sockun;
110 static struct sockaddr_un dataout;
111 int port=0;
112 char *group=NULL;
113 int sockno=0;
114 int res;
115 mode_t mode=0700;
116 char std_sockname[PATH_MAX];
117 char real_sockname[PATH_MAX];
118 char *sockname = real_sockname;
120 if (open_args != NULL) {
121 if (interface_version == 1) {
122 port=open_args->port;
123 group=open_args->group;
124 mode=open_args->mode;
126 else {
127 errno=EINVAL;
128 return NULL;
132 if ((conn=calloc(1,sizeof(struct vdeconn)))==NULL)
134 errno=ENOMEM;
135 return NULL;
137 //get the login name
138 callerpwd=getpwuid(getuid());
139 req.type = REQ_NEW_CONTROL;
140 if (given_sockname == NULL || *given_sockname == '\0') {
141 char *homedir = getenv("HOME");
142 given_sockname = NULL;
143 if (homedir) {
144 struct stat statbuf;
145 snprintf(std_sockname, PATH_MAX, "%s%s", homedir, STDSOCK);
146 if (lstat(std_sockname,&statbuf)==0)
147 given_sockname = std_sockname;
149 } else {
150 char *split;
151 if(given_sockname[strlen(given_sockname)-1] == ']' && (split=rindex(given_sockname,'[')) != NULL) {
152 *split=0;
153 split++;
154 port=atoi(split);
155 if (port == 0)
156 req.type = REQ_NEW_PORT0;
157 if (*given_sockname==0)
158 given_sockname = NULL;
162 /* Canonicalize the sockname: we need to send an absolute pathname to the
163 * switch (we don't know its cwd) for the data socket. Appending
164 * given_sockname to getcwd() would be enough, but we could end up with a
165 * name longer than PATH_MAX that couldn't be used as sun_path. */
166 if (given_sockname && vde_realpath(given_sockname, real_sockname) == NULL)
168 free(conn);
169 return NULL;
172 #ifdef USE_IPN
173 if((conn->fddata = socket(AF_IPN,SOCK_RAW,IPN_ANY)) >= 0) {
174 /* IPN service exists */
175 sockun.sun_family = AF_IPN;
177 if((conn->fddata = socket(AF_IPN_STOLEN,SOCK_RAW,IPN_ANY)) >= 0) {
178 /* IPN_STOLEN service exists */
179 sockun.sun_family = AF_IPN_STOLEN;
181 if (conn->fddata >= 0) {
182 if (port != 0 || req.type == REQ_NEW_PORT0)
183 setsockopt(conn->fddata,0,IPN_SO_PORT,&port,sizeof(port));
184 /* If we're given a sockname, just try it */
185 if (given_sockname)
187 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s", sockname);
188 res = connect(conn->fddata, (struct sockaddr *) &sockun, sizeof(sockun));
190 /* Else try all the fallback socknames, one by one */
191 else
193 int i;
194 for (i = 0, res = -1; fallback_sockname[i] && (res != 0); i++)
196 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s", fallback_sockname[i]);
197 res = connect(conn->fddata, (struct sockaddr *) &sockun, sizeof(sockun));
201 /* If one of the connect succeeded, we're done */
202 if (res == 0)
204 snprintf(req.description,MAXDESCR,"%s user=%s PID=%d %s",
205 descr,(callerpwd != NULL)?callerpwd->pw_name:"??",
206 pid,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"");
207 setsockopt(conn->fddata,0,IPN_SO_DESCR,req.description,
208 strlen(req.description+1));
209 *(conn->inpath.sun_path)=0; /*null string, do not delete "return path"*/
210 conn->fdctl=-1;
211 return conn;
214 #endif
215 if((conn->fdctl = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
216 free(conn);
217 return NULL;
219 if((conn->fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){
220 int err=errno;
221 close(conn->fdctl);
222 free(conn);
223 errno=err;
224 return NULL;
226 sockun.sun_family = AF_UNIX;
228 /* If we're given a sockname, just try it (remember: sockname is the
229 * canonicalized version of given_sockname - though we don't strictly need
230 * the canonicalized versiono here). sockname should be the name of a
231 * *directory* which contains the control socket, named ctl. Older
232 * versions of VDE used a socket instead of a directory (so an additional
233 * attempt with %s instead of %s/ctl could be made), but they should
234 * really not be used anymore. */
235 if (given_sockname)
237 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s/ctl", sockname);
238 res = connect(conn->fdctl, (struct sockaddr *) &sockun, sizeof(sockun));
240 /* Else try all the fallback socknames, one by one */
241 else
243 int i;
244 for (i = 0, res = -1; fallback_sockname[i] && (res != 0); i++)
246 /* Remember sockname for the data socket directory */
247 sockname = fallback_sockname[i];
248 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s", sockname);
249 res = connect(conn->fdctl, (struct sockaddr *) &sockun, sizeof(sockun));
253 if (res != 0)
255 int err = errno;
256 close(conn->fddata);
257 close(conn->fdctl);
258 free(conn);
259 errno = err;
260 return NULL;
263 req.magic=SWITCH_MAGIC;
264 req.version=3;
265 req.type=req.type+(port << 8);
266 req.sock.sun_family=AF_UNIX;
268 /* First choice, store the return socket from the switch in the control
269 * dir. We assume that given_sockname (hence sockname) is a directory.
270 * Should be a safe assumption unless someone modifies the previous group
271 * of connect() attempts (see the comments above for more information). */
272 memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
275 /* Here sockname is the last successful one in the previous step. */
276 sprintf(req.sock.sun_path, "%s/.%05d-%05d", sockname, pid, sockno++);
277 res=bind(conn->fddata, (struct sockaddr *) &req.sock, sizeof (req.sock));
279 while (res < 0 && errno == EADDRINUSE);
281 /* It didn't work. So we cycle on the fallback directories until we find a
282 * suitable one (or the list ends). */
283 if (res < 0)
285 int i;
286 for (i = 0, res = -1; fallback_dirname[i] && (res != 0); i++)
288 memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
291 sprintf(req.sock.sun_path, "%s/vde.%05d-%05d", fallback_dirname[i], pid, sockno++);
292 res = bind(conn->fddata, (struct sockaddr *) &req.sock, sizeof (req.sock));
294 while (res < 0 && errno == EADDRINUSE);
298 /* Nothing worked, so cleanup and return with an error. */
299 if (res < 0){
300 int err = errno;
301 close(conn->fddata);
302 close(conn->fdctl);
303 free(conn);
304 errno = err;
305 return NULL;
308 memcpy(&(conn->inpath),&req.sock,sizeof(req.sock));
309 if (group) {
310 struct group *gs;
311 gid_t gid;
312 if ((gs=getgrnam(group)) == NULL)
313 gid=atoi(group);
314 else
315 gid=gs->gr_gid;
316 chown(conn->inpath.sun_path,-1,gid);
318 chmod(conn->inpath.sun_path,mode);
320 snprintf(req.description,MAXDESCR,"%s user=%s PID=%d %s SOCK=%s",
321 descr,(callerpwd != NULL)?callerpwd->pw_name:"??",
322 pid,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"",req.sock.sun_path);
324 if (send(conn->fdctl,&req,sizeof(req)-MAXDESCR+strlen(req.description),0) < 0) {
325 int err=errno;
326 close(conn->fddata);
327 close(conn->fdctl);
328 free(conn);
329 errno=err;
330 return NULL;
333 if (recv(conn->fdctl,&(dataout),sizeof(struct sockaddr_un),0)<0) {
334 int err=errno;
335 close(conn->fddata);
336 close(conn->fdctl);
337 free(conn);
338 errno=err;
339 return NULL;
342 if (connect(conn->fddata,(struct sockaddr *)&(dataout),sizeof(struct sockaddr_un))<0) {
343 int err=errno;
344 close(conn->fddata);
345 close(conn->fdctl);
346 free(conn);
347 errno=err;
348 return NULL;
350 chmod(dataout.sun_path,mode);
352 return conn;
355 ssize_t vde_recv(VDECONN *conn,void *buf,size_t len,int flags)
357 if (__builtin_expect(conn!=0,1))
358 return recv(conn->fddata,buf,len,0);
359 else {
360 errno=EBADF;
361 return -1;
365 ssize_t vde_send(VDECONN *conn,const void *buf,size_t len,int flags)
367 if (__builtin_expect(conn!=0,1))
368 return send(conn->fddata,buf,len,0);
369 else {
370 errno=EBADF;
371 return -1;
375 int vde_datafd(VDECONN *conn)
377 if (__builtin_expect(conn!=0,1))
378 return conn->fddata;
379 else {
380 errno=EBADF;
381 return -1;
385 int vde_ctlfd(VDECONN *conn)
387 if (__builtin_expect(conn!=0,1))
388 return conn->fdctl;
389 else {
390 errno=EBADF;
391 return -1;
395 int vde_close(VDECONN *conn)
397 if (__builtin_expect(conn!=0,1)) {
398 if (*(conn->inpath.sun_path))
399 unlink(conn->inpath.sun_path);
400 close(conn->fddata);
401 close(conn->fdctl);
402 free(conn);
403 return 0;
404 } else {
405 errno=EBADF;
406 return -1;