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
21 #include <sys/socket.h>
29 #include <sys/types.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"
47 /* AF_IPN has not been officially assigned yet
48 we "steal" unused AF_NETBEUI in the meanwhile
49 this code will be uncommented when AF_IPN is assigned. */
51 #define AF_IPN 0 /* IPN sockets: */
57 #define AF_NETBEUI PF_NETBEUI
62 #define AF_IPN_STOLEN AF_NETBEUI /* IPN temporary sockets */
63 #define PF_IPN_STOLEN AF_IPN_STOLEN
67 #define IPN_SO_DESCR 1
70 /* Fallback names for the control socket, NULL-terminated array of absolute
72 char *fallback_sockname
[] = {
73 "/var/run/vde.ctl/ctl",
79 /* Fallback directories for the data socket, NULL-terminated array of absolute
80 * directory names, with no trailing /. */
81 const char *fallback_dirname
[] = {
91 struct sockaddr_un inpath
;
94 #define SWITCH_MAGIC 0xfeedface
97 enum request_type
{ REQ_NEW_CONTROL
, REQ_NEW_PORT0
};
102 enum request_type type
;
103 struct sockaddr_un sock
;
104 char description
[MAXDESCR
];
105 } __attribute__((packed
));
107 VDECONN
*vde_open_real(char *given_sockname
, char *descr
,int interface_version
,
108 struct vde_open_args
*open_args
)
110 struct vdeconn
*conn
=NULL
;
111 struct passwd
*callerpwd
;
112 struct request_v3
*req
=NULL
;
114 struct sockaddr_un
*sockun
=NULL
;
115 struct sockaddr_un
*dataout
=NULL
;
121 char *std_sockname
=NULL
;
122 char *real_sockname
=NULL
;
125 if (open_args
!= NULL
) {
126 if (interface_version
== 1) {
127 port
=open_args
->port
;
128 group
=open_args
->group
;
129 mode
=open_args
->mode
;
137 if ((req
=(struct request_v3
*)calloc(1, sizeof(struct request_v3
)))==NULL
) {
141 if ((sockun
=(struct sockaddr_un
*)calloc(1, sizeof(struct sockaddr_un
)))==NULL
) {
145 if ((dataout
=(struct sockaddr_un
*)calloc(1, sizeof(struct sockaddr_un
)))==NULL
) {
149 if ((std_sockname
=(char *)calloc(PATH_MAX
,sizeof(char)))==NULL
) {
153 if ((real_sockname
=(char *)calloc(PATH_MAX
,sizeof(char)))==NULL
) {
157 sockname
= real_sockname
;
158 if ((conn
=calloc(1,sizeof(struct vdeconn
)))==NULL
)
163 conn
->fdctl
=conn
->fddata
=-1;
165 callerpwd
=getpwuid(getuid());
166 req
->type
= REQ_NEW_CONTROL
;
167 if (given_sockname
== NULL
|| *given_sockname
== '\0') {
168 char *homedir
= getenv("HOME");
169 given_sockname
= NULL
;
172 snprintf(std_sockname
, PATH_MAX
, "%s%s", homedir
, STDSOCK
);
173 if (lstat(std_sockname
,&statbuf
)==0)
174 given_sockname
= std_sockname
;
178 if(given_sockname
[strlen(given_sockname
)-1] == ']' && (split
=rindex(given_sockname
,'[')) != NULL
) {
183 req
->type
= REQ_NEW_PORT0
;
184 if (*given_sockname
==0)
185 given_sockname
= NULL
;
189 /* Canonicalize the sockname: we need to send an absolute pathname to the
190 * switch (we don't know its cwd) for the data socket. Appending
191 * given_sockname to getcwd() would be enough, but we could end up with a
192 * name longer than PATH_MAX that couldn't be used as sun_path. */
193 if (given_sockname
&& vde_realpath(given_sockname
, real_sockname
) == NULL
)
198 /* AF_IPN has not been officially assigned yet
199 we "steal" unused AF_NETBEUI in the meanwhile
200 this code will be uncommented when AF_IPN is assigned. */
201 if((conn
->fddata
= socket(AF_IPN
,SOCK_RAW
,IPN_ANY
)) >= 0) {
202 /* IPN service exists */
203 sockun
.sun_family
= AF_IPN
;
206 if((conn
->fddata
= socket(AF_IPN_STOLEN
,SOCK_RAW
,IPN_ANY
)) >= 0) {
207 /* IPN_STOLEN service exists */
208 sockun
->sun_family
= AF_IPN_STOLEN
;
210 if (conn
->fddata
>= 0) {
211 if (port
!= 0 || req
->type
== REQ_NEW_PORT0
)
212 setsockopt(conn
->fddata
,0,IPN_SO_PORT
,&port
,sizeof(port
));
213 /* If we're given a sockname, just try it */
216 snprintf(sockun
->sun_path
, sizeof(sockun
->sun_path
), "%s", sockname
);
217 res
= connect(conn
->fddata
, (struct sockaddr
*) sockun
, sizeof(*sockun
));
219 /* Else try all the fallback socknames, one by one */
223 for (i
= 0, res
= -1; fallback_sockname
[i
] && (res
!= 0); i
++)
225 snprintf(sockun
->sun_path
, sizeof(sockun
->sun_path
), "%s", fallback_sockname
[i
]);
226 res
= connect(conn
->fddata
, (struct sockaddr
*) sockun
, sizeof(*sockun
));
230 /* If one of the connect succeeded, we're done */
233 snprintf(req
->description
,MAXDESCR
,"%s user=%s PID=%d %s",
234 descr
,(callerpwd
!= NULL
)?callerpwd
->pw_name
:"??",
235 pid
,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"");
236 setsockopt(conn
->fddata
,0,IPN_SO_DESCR
,req
->description
,
237 strlen(req
->description
+1));
238 *(conn
->inpath
.sun_path
)=0; /*null string, do not delete "return path"*/
244 if((conn
->fdctl
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0)
246 if((conn
->fddata
= socket(AF_UNIX
, SOCK_DGRAM
, 0)) < 0)
248 sockun
->sun_family
= AF_UNIX
;
250 /* If we're given a sockname, just try it (remember: sockname is the
251 * canonicalized version of given_sockname - though we don't strictly need
252 * the canonicalized versiono here). sockname should be the name of a
253 * *directory* which contains the control socket, named ctl. Older
254 * versions of VDE used a socket instead of a directory (so an additional
255 * attempt with %s instead of %s/ctl could be made), but they should
256 * really not be used anymore. */
259 snprintf(sockun
->sun_path
, sizeof(sockun
->sun_path
), "%s/ctl", sockname
);
260 res
= connect(conn
->fdctl
, (struct sockaddr
*) sockun
, sizeof(*sockun
));
262 /* Else try all the fallback socknames, one by one */
266 for (i
= 0, res
= -1; fallback_sockname
[i
] && (res
!= 0); i
++)
268 /* Remember sockname for the data socket directory */
269 sockname
= fallback_sockname
[i
];
270 snprintf(sockun
->sun_path
, sizeof(sockun
->sun_path
), "%s", sockname
);
271 res
= connect(conn
->fdctl
, (struct sockaddr
*) sockun
, sizeof(*sockun
));
278 req
->magic
=SWITCH_MAGIC
;
280 req
->type
=req
->type
+(port
<< 8);
281 req
->sock
.sun_family
=AF_UNIX
;
283 /* First choice, store the return socket from the switch in the control
284 * dir. We assume that given_sockname (hence sockname) is a directory.
285 * Should be a safe assumption unless someone modifies the previous group
286 * of connect() attempts (see the comments above for more information). */
287 memset(req
->sock
.sun_path
, 0, sizeof(req
->sock
.sun_path
));
290 /* Here sockname is the last successful one in the previous step. */
291 sprintf(req
->sock
.sun_path
, "%s/.%05d-%05d", sockname
, pid
, sockno
++);
292 res
=bind(conn
->fddata
, (struct sockaddr
*) &req
->sock
, sizeof (req
->sock
));
294 while (res
< 0 && errno
== EADDRINUSE
);
296 /* It didn't work. So we cycle on the fallback directories until we find a
297 * suitable one (or the list ends). */
301 for (i
= 0, res
= -1; fallback_dirname
[i
] && (res
!= 0); i
++)
303 memset(req
->sock
.sun_path
, 0, sizeof(req
->sock
.sun_path
));
306 sprintf(req
->sock
.sun_path
, "%s/vde.%05d-%05d", fallback_dirname
[i
], pid
, sockno
++);
307 res
= bind(conn
->fddata
, (struct sockaddr
*) &req
->sock
, sizeof (req
->sock
));
309 while (res
< 0 && errno
== EADDRINUSE
);
313 /* Nothing worked, so cleanup and return with an error. */
317 memcpy(&(conn
->inpath
),&req
->sock
,sizeof(req
->sock
));
321 if ((gs
=getgrnam(group
)) == NULL
)
325 chown(conn
->inpath
.sun_path
,-1,gid
);
327 /* when group is not defined, set permission for the reverse channel */
329 /* if no permission gets "voluntarily" granted to the socket */
330 if ((mode
& 077) == 0) {
331 if (stat(sockun
->sun_path
, &ctlstat
) == 0) {
332 /* if the switch is owned by root or by the same user it should
334 if (ctlstat
.st_uid
!= 0 && ctlstat
.st_uid
!= geteuid()) {
335 /* try to change the group ownership to the same of the switch */
336 /* this call succeeds if the vde user and the owner of the switch
337 belong to the group */
338 if (chown(conn
->inpath
.sun_path
,-1,ctlstat
.st_gid
) == 0)
346 chmod(conn
->inpath
.sun_path
,mode
);
348 snprintf(req
->description
,MAXDESCR
,"%s user=%s PID=%d %s SOCK=%s",
349 descr
,(callerpwd
!= NULL
)?callerpwd
->pw_name
:"??",
350 pid
,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"",req
->sock
.sun_path
);
352 if (send(conn
->fdctl
,req
,sizeof(*req
)-MAXDESCR
+strlen(req
->description
),0)<0)
355 if (recv(conn
->fdctl
,dataout
,sizeof(struct sockaddr_un
),0)<0)
358 if (connect(conn
->fddata
,(struct sockaddr
*)dataout
,sizeof(struct sockaddr_un
))<0)
361 chmod(dataout
->sun_path
,mode
);
369 if (conn
->fdctl
>= 0)
371 if (conn
->fddata
>= 0)
373 if (*(conn
->inpath
.sun_path
))
374 unlink(conn
->inpath
.sun_path
);
382 if (sockun
) free(sockun
);
383 if (dataout
) free(dataout
);
384 if (std_sockname
) free(std_sockname
);
385 if (real_sockname
) free(real_sockname
);
389 ssize_t
vde_recv(VDECONN
*conn
,void *buf
,size_t len
,int flags
)
391 if (__builtin_expect(conn
!=0,1))
392 return recv(conn
->fddata
,buf
,len
,0);
399 ssize_t
vde_send(VDECONN
*conn
,const void *buf
,size_t len
,int flags
)
401 if (__builtin_expect(conn
!=0,1))
402 return send(conn
->fddata
,buf
,len
,0);
409 int vde_datafd(VDECONN
*conn
)
411 if (__builtin_expect(conn
!=0,1))
419 int vde_ctlfd(VDECONN
*conn
)
421 if (__builtin_expect(conn
!=0,1))
429 int vde_close(VDECONN
*conn
)
431 if (__builtin_expect(conn
!=0,1)) {
432 if (*(conn
->inpath
.sun_path
))
433 unlink(conn
->inpath
.sun_path
);