From 48bc45f46be27c94dfe099f424683256c17fbb73 Mon Sep 17 00:00:00 2001 From: Benjamin Gordon Date: Thu, 5 Mar 2020 14:53:42 -0700 Subject: [PATCH] escl: Add unix socket support Curl can pass HTTP traffic through a unix socket instead of TCP with the CURLOPT_UNIX_SOCKET_PATH option. This is useful if you have a proxy listening on a local socket between your computer and the scanner. Now that URL parsing is centralized, take advantage of this to support an extended name scheme: escl:unix:/path/to/socket:URL These names won't be picked up on the network when avahi scans, but they can be passed manually with -d or added to /etc/sane.d/escl.conf. If the existing escl:URL syntax is used, the socket is not set and the curl option is not used, so existing network connections work the same as before. Since local sockets are optional, the current conf parsing scheme becomes order dependent. To avoid this, also add an additional "device" line type to the config parser. If a line starts with "device", the remainder of the line is treated as a complete device name/URL and parsed with the URL parsing added in the previous commit. --- backend/escl.conf.in | 6 +++ backend/escl/escl.c | 96 +++++++++++++++++++++++++++++++++++--------- backend/escl/escl.h | 1 + 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/backend/escl.conf.in b/backend/escl.conf.in index 2aa625771..9c482b573 100644 --- a/backend/escl.conf.in +++ b/backend/escl.conf.in @@ -8,6 +8,12 @@ # -> put your device ip instead of '123.456.789.10'. # -> put the port that you use instead of '88'. # For example, the lines below are for one device, but if you have several devices to use, you can duplicate the lines below as many times as you have devices. +# You can also configure a device on a single line starting with 'device' +# by writing a complete URL and an optional model name. + +#device http://123.456.789.10:8080 OptionalModel1 +#device https://123.456.789.10:443 "Optional Model 2" +#device unix:/run/proxy.sock:http://123.456.789.10:80 #[device] diff --git a/backend/escl/escl.c b/backend/escl/escl.c index 1afcc43a1..f109326fb 100644 --- a/backend/escl/escl.c +++ b/backend/escl/escl.c @@ -68,6 +68,7 @@ escl_free_device(ESCL_Device *current) free((void*)current->ip_address); free((void*)current->model_name); free((void*)current->type); + free(current->unix_socket); free(current); return NULL; } @@ -82,6 +83,8 @@ escl_free_handler(escl_sane_t *handler) free(handler); } +SANE_Status escl_parse_name(SANE_String_Const name, ESCL_Device *device); + static SANE_Status escl_check_and_add_device(ESCL_Device *current) { @@ -211,23 +214,31 @@ max_string_size(const SANE_String_Const strings[]) static SANE_Device * convertFromESCLDev(ESCL_Device *cdev) { - char tmp[PATH_MAX] = { 0 }; + char *tmp; + int len; + char unix_path[PATH_MAX+7] = { 0 }; SANE_Device *sdev = (SANE_Device*) calloc(1, sizeof(SANE_Device)); if (!sdev) { DBG (10, "Sane_Device allocation failure.\n"); return NULL; } - if (!cdev->https) - snprintf(tmp, sizeof(tmp), "http://%s:%d", cdev->ip_address, cdev->port_nb); - else - snprintf(tmp, sizeof(tmp), "https://%s:%d", cdev->ip_address, cdev->port_nb); - DBG( 1, "Escl add device : %s\n", tmp); - sdev->name = strdup(tmp); - if (!sdev->name) { - DBG (10, "Name allocation failure.\n"); - goto freedev; + if (cdev->unix_socket && strlen(cdev->unix_socket)) { + snprintf(unix_path, sizeof(unix_path), "unix:%s:", cdev->unix_socket); } + len = snprintf(NULL, 0, "%shttp%s://%s:%d", + unix_path, cdev->https ? "s" : "", cdev->ip_address, cdev->port_nb); + len++; + tmp = (char *)malloc(len); + if (!tmp) { + DBG (10, "Name allocation failure.\n"); + goto freedev; + } + snprintf(tmp, len, "%shttp%s://%s:%d", + unix_path, cdev->https ? "s" : "", cdev->ip_address, cdev->port_nb); + sdev->name = tmp; + + DBG( 1, "Escl add device : %s\n", tmp); sdev->model = strdup(cdev->model_name); if (!sdev->model) { DBG (10, "Model allocation failure.\n"); @@ -319,42 +330,75 @@ attach_one_config(SANEI_Config __sane_unused__ *config, const char *line) SANE_Status status; static ESCL_Device *escl_device = NULL; + if (strncmp(line, "device", 6) == 0) { + char *name_str = NULL; + char *opt_model = NULL; + + line = sanei_config_get_string(line + 6, &name_str); + DBG (10, "New Escl_Device URL [%s].\n", (name_str ? name_str : "VIDE")); + if (!name_str || !*name_str) { + DBG (1, "Escl_Device URL missing.\n"); + return SANE_STATUS_INVAL; + } + if (*line) { + line = sanei_config_get_string(line, &opt_model); + DBG (10, "New Escl_Device model [%s].\n", opt_model); + } + + escl_free_device(escl_device); + escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device)); + if (!escl_device) { + DBG (10, "New Escl_Device allocation failure.\n"); + free(name_str); + return (SANE_STATUS_NO_MEM); + } + status = escl_parse_name(name_str, escl_device); + free(name_str); + if (status != SANE_STATUS_GOOD) { + escl_free_device(escl_device); + escl_device = NULL; + return status; + } + escl_device->model_name = opt_model ? opt_model : strdup("Unknown model"); + escl_device->type = strdup("flatbed scanner"); + } + if (strncmp(line, "[device]", 8) == 0) { escl_device = escl_free_device(escl_device); escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device)); if (!escl_device) { - DBG (10, "New Escl_Device allocation failure."); + DBG (10, "New Escl_Device allocation failure.\n"); return (SANE_STATUS_NO_MEM); } } if (strncmp(line, "ip", 2) == 0) { const char *ip_space = sanei_config_skip_whitespace(line + 2); - DBG (10, "New Escl_Device IP [%s].", (ip_space ? ip_space : "VIDE")); + DBG (10, "New Escl_Device IP [%s].\n", (ip_space ? ip_space : "VIDE")); if (escl_device != NULL && ip_space != NULL) { - DBG (10, "New Escl_Device IP Affected."); + DBG (10, "New Escl_Device IP Affected.\n"); escl_device->ip_address = strdup(ip_space); } } if (sscanf(line, "port %i", &port) == 1 && port != 0) { - DBG (10, "New Escl_Device PORT [%d].", port); + DBG (10, "New Escl_Device PORT [%d].\n", port); if (escl_device != NULL) { - DBG (10, "New Escl_Device PORT Affected."); + DBG (10, "New Escl_Device PORT Affected.\n"); escl_device->port_nb = port; } } if (strncmp(line, "model", 5) == 0) { const char *model_space = sanei_config_skip_whitespace(line + 5); - DBG (10, "New Escl_Device MODEL [%s].", (model_space ? model_space : "VIDE")); + DBG (10, "New Escl_Device MODEL [%s].\n", (model_space ? model_space : "VIDE")); if (escl_device != NULL && model_space != NULL) { - DBG (10, "New Escl_Device MODEL Affected."); + DBG (10, "New Escl_Device MODEL Affected.\n"); escl_device->model_name = strdup(model_space); } } if (strncmp(line, "type", 4) == 0) { const char *type_space = sanei_config_skip_whitespace(line + 4); - DBG (10, "New Escl_Device TYPE [%s].", (type_space ? type_space : "VIDE")); + DBG (10, "New Escl_Device TYPE [%s].\n", (type_space ? type_space : "VIDE")); if (escl_device != NULL && type_space != NULL) { - DBG (10, "New Escl_Device TYPE Affected."); + DBG (10, "New Escl_Device TYPE Affected.\n"); escl_device->type = strdup(type_space); } } @@ -551,6 +595,15 @@ escl_parse_name(SANE_String_Const name, ESCL_Device *device) return SANE_STATUS_INVAL; } + if (strncmp(name, "unix:", 5) == 0) { + SANE_String_Const socket = name + 5; + name = strchr(socket, ':'); + if (name == NULL) + return SANE_STATUS_INVAL; + device->unix_socket = strndup(socket, name - socket); + name++; + } + if (strncmp(name, "https://", 8) == 0) { device->https = SANE_TRUE; host = name + 8; @@ -1005,4 +1058,9 @@ escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path) curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L); } + if (device->unix_socket != NULL) { + DBG( 1, "Using local socket %s\n", device->unix_socket ); + curl_easy_setopt(handle, CURLOPT_UNIX_SOCKET_PATH, + device->unix_socket); + } } diff --git a/backend/escl/escl.h b/backend/escl/escl.h index f6c658575..664fed5b6 100644 --- a/backend/escl/escl.h +++ b/backend/escl/escl.h @@ -84,6 +84,7 @@ typedef struct ESCL_Device { char *ip_address; char *type; SANE_Bool https; + char *unix_socket; } ESCL_Device; typedef struct capabilities