X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fafb-ws-client.c;h=d1cea8c71d06defdd28b024fa3f68855b672b8de;hb=65353dce81a629e042800bb7b86fcd869a76727e;hp=6ed3f50fcd7b165e77cf8fdffd856e3f4b93ae60;hpb=7b97a7f27578d7f4ce7cc994e5df303ac5d9c886;p=src%2Fapp-framework-binder.git diff --git a/src/afb-ws-client.c b/src/afb-ws-client.c index 6ed3f50f..d1cea8c7 100644 --- a/src/afb-ws-client.c +++ b/src/afb-ws-client.c @@ -1,5 +1,5 @@ /* - * Copyright 2016 IoT.bzh + * Copyright (C) 2015-2020 "IoT.bzh" * Author: José Bollo * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,8 +26,10 @@ #include #include #include +#include #include "afb-wsj1.h" +#include "fdev-systemd.h" /**************** WebSocket handshake ****************************/ @@ -50,14 +52,6 @@ static const char *compkeys[32] = { "MHiEc+Qc8w/SJ3zMHEM8pA==", "FVCxLBmoil3gY0jSX3aNJ6kR/t4=" }; -static const char websocket_s[] = "websocket"; -static const char sec_websocket_key_s[] = "Sec-WebSocket-Key"; -static const char sec_websocket_version_s[] = "Sec-WebSocket-Version"; -static const char sec_websocket_accept_s[] = "Sec-WebSocket-Accept"; -static const char sec_websocket_protocol_s[] = "Sec-WebSocket-Protocol"; - -static const char vseparators[] = " \t,"; - /* get randomly a pair of key/accept value */ static void getkeypair(const char **key, const char **ack) { @@ -82,8 +76,12 @@ static char *strjoin(int count, const char **strings, const char *separ) for(count = 0 ; strings[count] != NULL ; count++); /* compute the length of the result */ - length = 0; - if (count != 0) { + if (count == 0) + length = 0; + else { + length = (unsigned)(count - 1) * strlen(separ); + for (idx = 0 ; idx < count ; idx ++) + length += strlen(strings[idx]); } /* allocates the result */ @@ -104,10 +102,11 @@ static char *strjoin(int count, const char **strings, const char *separ) } /* creates the http message for the request */ -static int make_request(char **request, const char *path, const char *key, const char *protocols) +static int make_request(char **request, const char *path, const char *host, const char *key, const char *protocols) { - int rc = asprintf(request, - "GET %s HTTP1.1\r\n" + int rc = asprintf(request, + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Version: 13\r\n" @@ -116,6 +115,7 @@ static int make_request(char **request, const char *path, const char *key, const "Content-Length: 0\r\n" "\r\n" , path + , host , key , protocols ); @@ -128,7 +128,7 @@ static int make_request(char **request, const char *path, const char *key, const } /* create the request and send it to fd, returns the expected accept string */ -static const char *send_request(int fd, const char **protocols, const char *path) +static const char *send_request(int fd, const char **protocols, const char *path, const char *host) { const char *key, *ack; char *protolist, *request; @@ -141,7 +141,7 @@ static const char *send_request(int fd, const char **protocols, const char *path /* create the request */ getkeypair(&key, &ack); - length = make_request(&request, path, key, protolist); + length = make_request(&request, path, host, key, protolist); free(protolist); if (length < 0) return NULL; @@ -194,15 +194,15 @@ static int receive_response(int fd, const char **protocols, const char *ack) goto error; len = strcspn(line, " "); if (len != 8 || 0 != strncmp(line, "HTTP/1.1", 8)) - goto error; + goto abort; it = line + len; - len = strspn(line, " "); + len = strspn(it, " "); if (len == 0) - goto error; + goto abort; it += len; - len = strcspn(line, " "); - if (len != 3 || 0 != strncmp(line, "101", 3)) - goto error; + len = strcspn(it, " "); + if (len != 3 || 0 != strncmp(it, "101", 3)) + goto abort; /* reads the rest of the response until empty line */ clen = 0; @@ -243,20 +243,18 @@ static int receive_response(int fd, const char **protocols, const char *ack) if (clen > 0) { while (read(fd, line, len) < 0 && errno == EINTR); } - if (haserr != 0) - result = -1; - else if (result < 0) { - result = 0; - while(protocols[result] != NULL) - result++; - } -error: + if (haserr != 0 || result < 0) + goto abort; return result; +abort: + errno = ECONNABORTED; +error: + return -1; } -static int negociate(int fd, const char **protocols, const char *path) +static int negociate(int fd, const char **protocols, const char *path, const char *host) { - const char *ack = send_request(fd, protocols, path); + const char *ack = send_request(fd, protocols, path, host); return ack == NULL ? -1 : receive_response(fd, protocols, ack); } @@ -296,8 +294,7 @@ static int parse_uri(const char *uri, char **host, char **service, const char ** /* make the result */ *host = strndup(h, hlen); if (*host != NULL) { - return -1; - *service = plen ? strndup(h, hlen) : strdup("http"); + *service = plen ? strndup(p, plen) : strdup("http"); if (*service != NULL) { *path = uri; return 0; @@ -310,7 +307,6 @@ invalid: errno = EINVAL; error: return -1; - } @@ -318,13 +314,14 @@ error: static const char *proto_json1[2] = { "x-afb-ws-json1", NULL }; -struct afb_wsj1 *afb_ws_client_connect_wsj1(const char *uri, struct afb_wsj1_itf *itf, void *closure) +struct afb_wsj1 *afb_ws_client_connect_wsj1(struct sd_event *eloop, const char *uri, struct afb_wsj1_itf *itf, void *closure) { int rc, fd; - char *host, *service; + char *host, *service, xhost[32]; const char *path; struct addrinfo hint, *rai, *iai; struct afb_wsj1 *result; + struct fdev *fdev; /* scan the uri */ rc = parse_uri(uri, &host, &service, &path); @@ -347,15 +344,26 @@ struct afb_wsj1 *afb_ws_client_connect_wsj1(const char *uri, struct afb_wsj1_itf result = NULL; iai = rai; while (iai != NULL) { + struct sockaddr_in *a = (struct sockaddr_in*)(iai->ai_addr); + unsigned char *ipv4 = (unsigned char*)&(a->sin_addr.s_addr); + unsigned char *port = (unsigned char*)&(a->sin_port); + sprintf(xhost, "%d.%d.%d.%d:%d", + (int)ipv4[0], (int)ipv4[1], (int)ipv4[2], (int)ipv4[3], + (((int)port[0]) << 8)|(int)port[1]); fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol); if (fd >= 0) { rc = connect(fd, iai->ai_addr, iai->ai_addrlen); if (rc == 0) { - rc = negociate(fd, proto_json1, path); + rc = negociate(fd, proto_json1, path, xhost); if (rc == 0) { - result = afb_wsj1_create(fd, itf, closure); - if (result != NULL) - break; + fdev = fdev_systemd_create(eloop, fd); + if (fdev) { + result = afb_wsj1_create(fdev, itf, closure); + if (result != NULL) { + fcntl(fd, F_SETFL, O_NONBLOCK); + break; + } + } } } close(fd); @@ -394,4 +402,130 @@ static char *makequery(const char *path, const char *uuid, const char *token) } #endif +/*****************************************************************************************************************************/ + +#include +#include "afb-proto-ws.h" + +static int get_socket_unix(const char *uri) +{ + int fd, rc; + struct sockaddr_un addr; + size_t length; + + length = strlen(uri); + if (length >= 108) { + errno = ENAMETOOLONG; + return -1; + } + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return fd; + + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, uri); + if (addr.sun_path[0] == '@') + addr.sun_path[0] = 0; /* implement abstract sockets */ + rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr)); + if (rc < 0) { + close(fd); + return rc; + } + return fd; +} + +static int get_socket_inet(const char *uri) +{ + int rc, fd; + const char *service, *host, *api; + struct addrinfo hint, *rai, *iai; + + /* scan the uri */ + service = strrchr(uri, ':'); + if (service == NULL) { + errno = EINVAL; + return -1; + } + api = strchrnul(service, '/'); + host = strndupa(uri, service++ - uri); + service = strndupa(service, api - service); + + /* get addr */ + memset(&hint, 0, sizeof hint); + hint.ai_family = AF_INET; + hint.ai_socktype = SOCK_STREAM; + rc = getaddrinfo(host, service, &hint, &rai); + if (rc != 0) { + errno = EINVAL; + return -1; + } + + /* get the socket */ + iai = rai; + while (iai != NULL) { + fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol); + if (fd >= 0) { + rc = connect(fd, iai->ai_addr, iai->ai_addrlen); + if (rc == 0) { + freeaddrinfo(rai); + return fd; + } + close(fd); + } + iai = iai->ai_next; + } + freeaddrinfo(rai); + return -1; +} + +static int get_socket(const char *uri) +{ + int fd; + + /* check for unix socket */ + if (0 == strncmp(uri, "unix:", 5)) + /* unix socket */ + fd = get_socket_unix(uri + 5); + else if (0 == strncmp(uri, "tcp:", 4)) + /* unix socket */ + fd = get_socket_inet(uri + 4); + else + /* inet socket */ + fd = get_socket_inet(uri); + + /* configure the socket */ + if (fd >= 0) { + fcntl(fd, F_SETFD, FD_CLOEXEC); + fcntl(fd, F_SETFL, O_NONBLOCK); + } + return fd; +} + +/* + * Establish a websocket-like client connection to the API of 'uri' and if successful + * instantiate a client afb_proto_ws websocket for this API using 'itf' and 'closure'. + * (see afb_proto_ws_create_client). + * The systemd event loop 'eloop' is used to handle the websocket. + * Returns NULL in case of failure with errno set appropriately. + */ +struct afb_proto_ws *afb_ws_client_connect_api(struct sd_event *eloop, const char *uri, struct afb_proto_ws_client_itf *itf, void *closure) +{ + int fd; + struct afb_proto_ws *pws; + struct fdev *fdev; + + fd = get_socket(uri); + if (fd >= 0) { + fdev = fdev_systemd_create(eloop, fd); + if (fdev) { + pws = afb_proto_ws_create_client(fdev, itf, closure); + if (pws) + return pws; + } + close(fd); + } + return NULL; +}