diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/libpies.h | 3 | ||||
-rw-r--r-- | lib/urlconn.c | 157 |
3 files changed, 161 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 7b4eb42..4eb6041 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -35,2 +35,3 @@ libpies_a_SOURCES=\ url.c\ + urlconn.c\ wildmatch.c diff --git a/lib/libpies.h b/lib/libpies.h index 76b70fe..40364a6 100644 --- a/lib/libpies.h +++ b/lib/libpies.h @@ -104,2 +104,5 @@ char *sockaddr_to_astr (const struct sockaddr *sa, int salen); +/* urlconn.c */ +int url_connect (struct pies_url *url, struct grecs_sockaddr *source_addr); + diff --git a/lib/urlconn.c b/lib/urlconn.c new file mode 100644 index 0000000..5994626 --- /dev/null +++ b/lib/urlconn.c @@ -0,0 +1,157 @@ +/* This file is part of GNU Pies. + Copyright (C) 2019 Sergey Poznyakoff + + GNU Pies is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Pies is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <netdb.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <libpies.h> +#include <grecs.h> + +int +url_connect (struct pies_url *url, struct grecs_sockaddr *source_addr) +{ + int fd; + union pies_sockaddr_storage addr; + socklen_t socklen; + int flags; + + if (strcmp (url->scheme, "unix") == 0 + || strcmp (url->scheme, "file") == 0 + || strcmp (url->scheme, "socket") == 0) + { + struct stat st; + + if (url->port) + { + grecs_error (NULL, 0, _("%s: invalid connection type: " + "port is meaningless for UNIX sockets"), + url->string); + errno = EINVAL; + return -1; + } + + if (strlen (url->path) > sizeof addr.s_un.sun_path) + { + grecs_error (NULL, 0, + _("%s: UNIX socket name too long"), + url->path); + errno = EINVAL; + return -1; + } + + addr.s.sa_family = PF_UNIX; + socklen = sizeof (addr.s_un); + strcpy (addr.s_un.sun_path, url->path); + + if (stat (url->path, &st)) + { + if (errno != ENOENT) + { + grecs_error (NULL, errno, _("%s: %s failed"), url->path, "stat"); + return -1; + } + } + else + { + if (!S_ISSOCK (st.st_mode)) + { + grecs_error (NULL, 0, _("%s: not a socket"), url->path); + errno = EINVAL; + return -1; + } + } + } + else if (strcmp (url->scheme, "inet") == 0) + { + struct addrinfo hints; + struct addrinfo *res; + int rc; + + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_family = AF_INET; + + rc = getaddrinfo (url->host, url->port_s, &hints, &res); + switch (rc) + { + case 0: + break; + + case EAI_SYSTEM: + grecs_error (NULL, errno, "%s", _("cannot parse address")); + return -1; + + case EAI_BADFLAGS: + case EAI_SOCKTYPE: + grecs_error (NULL, 0, _("%s:%d: internal error converting address"), + __FILE__,__LINE__); + return -1; + + case EAI_MEMORY: + grecs_alloc_die (); + + default: + grecs_error (NULL, 0, "getaddrinfo: %s", gai_strerror(rc)); + return -1; + } + + socklen = sizeof (addr.s_in); + memcpy (&addr.s_in, res->ai_addr, socklen); + freeaddrinfo (res); + } + + fd = socket (addr.s.sa_family, SOCK_STREAM, 0); + if (fd == -1) + { + grecs_error (NULL, errno, _("%s: %s failed"), url->string, "socket"); + return -1; + } + + if ((flags = fcntl (fd, F_GETFD, 0)) == -1 + || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1) + grecs_error (NULL, 0, _("%s: cannot set close-on-exec: %s"), + url->string, strerror (errno)); + + if (source_addr) + { + if (source_addr->sa->sa_family != addr.s.sa_family) + grecs_error (NULL, 0, + _("source and destination address family differ")); + else if (bind (fd, source_addr->sa, source_addr->len) < 0) + { + grecs_error (NULL, errno, _("%s: %s failed"), url->string, "bind"); + close (fd); + return -1; + } + } + + if (connect (fd, &addr.s, socklen)) + { + grecs_error (NULL, errno, _("%s: %s failed"), url->string, "connect"); + close (fd); + return -1; + } + + return fd; +} |