/* * httpcmd.c - Part of AFD, an automatic file distribution program. * Copyright (c) 2003 - 2024 Holger Kiehl * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "afddefs.h" DESCR__S_M3 /* ** NAME ** httpcmd - commands to send and retrieve files via HTTP ** ** SYNOPSIS ** int http_connect(char *hostname, char *http_proxy, int port, ** char *user, char *passwd, unsigned char auth_type, ** int features, unsigned char service, char *region, ** int ssl, int sndbuf_size, int rcvbuf_size, char debug, ** int init_hmr) ** int http_init_authentication(char *user, char *passwd) ** int http_options(char *path) ** int http_del(char *path, char *filename) ** int http_get(char *path, char *filename, char *new_filename, ** char *etag, off_t *content_length, off_t offset) ** int http_head(char *path, char *filename, off_t *content_length, ** time_t date) ** int http_noop(char *path) ** int http_put(char *path, char *filename, char *fullname, ** off_t file_size, char *file_content_hash_hex, ** int first_file) ** int http_put_response(void) ** int http_read(char *block, int blocksize) ** int http_chunk_read(char **chunk, int *chunksize) ** int http_version(void) ** int http_write(char *block, char *buffer, int size) ** void http_quit(void) ** ** DESCRIPTION ** httpcmd provides a set of commands to communicate with an HTTP ** server via BSD sockets. ** ** ** RETURN VALUES ** Returns SUCCESS when successful. When an error has occurred ** it will either return INCORRECT or the three digit HTTP reply ** code when the reply of the server does not conform to the ** one expected. The complete reply string of the HTTP server ** is returned to 'msg_str'. 'timeout_flag' is just a flag to ** indicate that the 'transfer_timeout' time has been reached. ** ** AUTHOR ** H.Kiehl ** ** HISTORY ** 19.04.2003 H.Kiehl Created ** 25.12.2003 H.Kiehl Added traceing. ** 28.04.2004 H.Kiehl Handle chunked reading. ** 16.08.2006 H.Kiehl Added http_head() function. ** 17.08.2006 H.Kiehl Added appending to http_get(). ** 12.08.2012 H.Kiehl Use getaddrinfo() instead of gethostname() to ** support IPv6. ** 11.09.2014 H.Kiehl Added simulation mode. ** 03.11.2018 H.Kiehl Implemented ServerNameIndication for TLS. ** 23.03.2021 H.Kiehl Added etag support for http_get(). ** 25.07.2021 H.Kiehl Added http_init_basic_authentication(). ** 07.08.2021 H.Kiehl Put authentication into new authcmd.c. ** 19.03.2022 H.Kiehl Add strict TLS support. ** 17.07.2022 H.Kiehl Add option to enable TLS legacy renegotiation. ** 18.10.2022 H.Kiehl Add support for HTTP digest authentication. ** 12.01.2024 H.Kiehl For http_head() see 204+206 as success. */ DESCR__E_M3 #include /* fprintf() */ #include /* va_start(), va_arg(), va_end() */ #include /* memset(), memcpy(), strcpy() */ #include /* strtol(), free() */ #include /* isdigit() */ #include /* fd_set */ #include /* struct timeval */ #include #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include /* socket(), shutdown(), bind(), setsockopt() */ #endif #ifdef HAVE_NETINET_IN_H # include /* struct in_addr, sockaddr_in, htons() */ #endif #if defined (_WITH_TOS) && defined (IP_TOS) # ifdef HAVE_NETINET_IN_SYSTM_H # include # endif # ifdef HAVE_NETINET_IP_H # include /* IPTOS_LOWDELAY, IPTOS_THROUGHPUT */ # endif #endif #ifdef HAVE_ARPA_TELNET_H # include /* IAC, SYNCH, IP */ #endif #ifdef HAVE_NETDB_H # include /* struct hostent, gethostbyname() */ #endif #ifdef HAVE_ARPA_INET_H # include /* inet_addr() */ #endif #ifdef WITH_SSL # include /* sigsetjmp(), siglongjmp() */ # include /* signal() */ # include # include # include #endif #include /* select(), write(), read(), close(), alarm() */ #include #include "fddefs.h" #include "commondefs.h" #include "httpdefs.h" #define FUB_DEBUG 1 /* Remove once bug is found! */ #ifdef WITH_SSL SSL *ssl_con = NULL; #endif /* External global variables. */ extern int simulation_mode, timeout_flag; #ifdef WITH_IP_DB extern int use_ip_db; #endif extern unsigned int special_flag; extern char msg_str[]; #ifdef LINUX extern char *h_errlist[]; /* for gethostbyname() */ extern int h_nerr; /* for gethostbyname() */ #endif extern long transfer_timeout; /* Local global variables. */ static int http_fd; #ifdef WITH_SSL static sigjmp_buf env_alrm; static SSL_CTX *ssl_ctx = NULL; #endif static struct timeval timeout; static struct http_message_reply hmr; /* Local function prototypes. */ static int check_connection(void), flush_read(char *), get_http_reply(int *, int, int), init_authentication(char *, char *, char *), #ifdef WITH_SSL prepare_aws4_listing(char *, char *, char *, int *), #endif read_msg(int *, int, int), store_http_digest(int, int); static void clear_msg_str(void), read_last_chunk(void), #ifdef WITH_SSL sig_handler(int), #endif #ifdef _WITH_EXTRA_CHECK store_http_etag(int, int), #endif store_http_options(int, int); /*########################## http_connect() #############################*/ int http_connect(char *hostname, char *http_proxy, int port, char *user, char *passwd, unsigned char auth_type, unsigned int fra_options, int features, #ifdef WITH_SSL unsigned char list_version, unsigned char service, char *region, char tls_auth, #endif int sndbuf_size, int rcvbuf_size, char debug, int init_hmr) { if (simulation_mode == YES) { if ((http_fd = open("/dev/null", O_RDWR)) == -1) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", "Simulated http_connect()", _("Failed to open() /dev/null : %s"), strerror(errno)); return(INCORRECT); } else { #ifdef WITH_TRACE int length; length = snprintf(msg_str, MAX_RET_MSG_LENGTH, #else (void)snprintf(msg_str, MAX_RET_MSG_LENGTH, #endif _("Simulated HTTP connect to %s%s (port=%d)"), (http_proxy[0] == '\0') ? "" : "proxy ", (http_proxy[0] == '\0') ? hostname : http_proxy, port); #ifdef WITH_TRACE if (length > MAX_RET_MSG_LENGTH) { length = MAX_RET_MSG_LENGTH; } trace_log(NULL, 0, C_TRACE, msg_str, length, NULL); #endif (void)my_strncpy(hmr.hostname, hostname, MAX_REAL_HOSTNAME_LENGTH + 1); (void)my_strncpy(hmr.http_proxy, http_proxy, MAX_REAL_HOSTNAME_LENGTH + 1); (void)my_strncpy(hmr.user, user, MAX_USER_NAME_LENGTH + 1); (void)my_strncpy(hmr.passwd, passwd, MAX_USER_NAME_LENGTH + 1); #ifdef HTTP_NO_AUTO_BASIC_AUTH if (auth_type == AUTH_BASIC) { hmr.www_authenticate = WWW_AUTHENTICATE_BASIC; } else { hmr.www_authenticate = WWW_AUTHENTICATE_UNKNOWN; } #else if (auth_type == AUTH_DIGEST) { hmr.www_authenticate = WWW_AUTHENTICATE_UNKNOWN; } else { hmr.www_authenticate = WWW_AUTHENTICATE_BASIC; } #endif hmr.digest_options = 0; if (init_hmr == YES) { hmr.authorization = NULL; hmr.realm = NULL; hmr.nonce = NULL; hmr.opaque = NULL; } hmr.auth_type = auth_type; hmr.port = port; hmr.free = YES; hmr.fra_options = fra_options; hmr.features = features; hmr.debug = debug; #ifdef WITH_SSL hmr.service_type = service; hmr.marker[0] = '\0'; hmr.marker_length = 0; if (list_version == '1') { hmr.listobject_version = '1'; } else { hmr.listobject_version = DEFAULT_AWS4_LIST_VERSION; } if ((service == SERVICE_S3) || (service == SERVICE_NONE)) { /* s3 */ hmr.service[0] = 's'; hmr.service[1] = '3'; hmr.service[2] = '\0'; } else { hmr.service[0] = '\0'; } if (region[0] == '\0') { (void)my_strncpy(hmr.region, DEFAULT_REGION_STR, MAX_REAL_HOSTNAME_LENGTH + 1); } else { (void)my_strncpy(hmr.region, region, MAX_REAL_HOSTNAME_LENGTH + 1); } #endif hmr.http_version = 0; hmr.http_options = 0; hmr.http_options_not_working = 0; hmr.bytes_buffered = 0; hmr.bytes_read = 0; if (sndbuf_size > 0) { hmr.sndbuf_size = sndbuf_size; } else { hmr.sndbuf_size = 0; } if (rcvbuf_size > 0) { hmr.rcvbuf_size = rcvbuf_size; } else { hmr.rcvbuf_size = 0; } if (init_hmr == YES) { hmr.filename = NULL; } #ifdef _WITH_EXTRA_CHECK hmr.http_etag[0] = '\0'; hmr.http_weak_etag = YES; #endif } } else { #ifdef WITH_TRACE int length; #endif #ifdef WITH_IP_DB int ip_from_db = NO; #endif char *p_hostname; #if defined (HAVE_GETADDRINFO) && defined (HAVE_GAI_STRERROR) int reply; char str_port[MAX_INT_LENGTH]; struct addrinfo hints, *result = NULL, *rp; if (http_proxy[0] == '\0') { p_hostname = hostname; } else { p_hostname = http_proxy; } (void)memset((struct addrinfo *) &hints, 0, sizeof(struct addrinfo)); hints.ai_family = (special_flag & DISABLE_IPV6_FLAG) ? AF_INET : AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; /* ??? hints.ai_flags = AI_CANONNAME; */ (void)snprintf(str_port, MAX_INT_LENGTH, "%d", port); reply = getaddrinfo(p_hostname, str_port, &hints, &result); if (reply != 0) { # ifdef WITH_IP_DB char ip_str[MAX_REAL_HOSTNAME_LENGTH + 10]; if (((reply == EAI_NONAME) || (reply == EAI_SYSTEM) || (reply == EAI_AGAIN)) && (use_ip_db == YES) && (lookup_ip_from_ip_db(hostname, ip_str, MAX_REAL_HOSTNAME_LENGTH + 10) == SUCCESS)) { reply = getaddrinfo(ip_str, str_port, &hints, &result); if (reply != 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to getaddrinfo() %s : %s"), ip_str, gai_strerror(reply)); freeaddrinfo(result); return(INCORRECT); } ip_from_db = YES; } else { # endif trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to getaddrinfo() %s : %s"), p_hostname, gai_strerror(reply)); freeaddrinfo(result); return(INCORRECT); # ifdef WITH_IP_DB } # endif } /* * getaddrinfo() returns a list of address structures. * Try each address until we successfully connect(). If socket() * (or connect()) fails, we (close the socket and) try the next * address. */ for (rp = result; rp != NULL; rp = rp->ai_next) { if ((http_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) { # ifdef WITH_TRACE length = snprintf(msg_str, MAX_RET_MSG_LENGTH, _("socket() error : %s"), strerror(errno)); if (length > MAX_RET_MSG_LENGTH) { length = MAX_RET_MSG_LENGTH; } trace_log(NULL, 0, C_TRACE, msg_str, length, NULL); # endif continue; } if (sndbuf_size > 0) { if (setsockopt(http_fd, SOL_SOCKET, SO_SNDBUF, (char *)&sndbuf_size, sizeof(sndbuf_size)) < 0) { trans_log(WARN_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("setsockopt() error : %s"), strerror(errno)); } hmr.sndbuf_size = sndbuf_size; } else { hmr.sndbuf_size = 0; } if (rcvbuf_size > 0) { if (setsockopt(http_fd, SOL_SOCKET, SO_RCVBUF, (char *)&rcvbuf_size, sizeof(rcvbuf_size)) < 0) { trans_log(WARN_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("setsockopt() error : %s"), strerror(errno)); } hmr.rcvbuf_size = rcvbuf_size; } else { hmr.rcvbuf_size = 0; } # ifdef FTP_CTRL_KEEP_ALIVE_INTERVAL if (timeout_flag != OFF) { reply = 1; if (setsockopt(http_fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&reply, sizeof(reply)) < 0) { trans_log(WARN_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("setsockopt() SO_KEEPALIVE error : %s"), strerror(errno)); } # ifdef TCP_KEEPALIVE reply = timeout_flag; if (setsockopt(http_fd, IPPROTO_IP, TCP_KEEPALIVE, (char *)&reply, sizeof(reply)) < 0) { trans_log(WARN_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("setsockopt() TCP_KEEPALIVE error : %s"), strerror(errno)); } # endif timeout_flag = OFF; } # endif reply = connect_with_timeout(http_fd, rp->ai_addr, rp->ai_addrlen); if (reply == INCORRECT) { if (errno != 0) { # ifdef WITH_TRACE length = snprintf(msg_str, MAX_RET_MSG_LENGTH, _("connect() error : %s"), strerror(errno)); if (length > MAX_RET_MSG_LENGTH) { length = MAX_RET_MSG_LENGTH; } trace_log(NULL, 0, C_TRACE, msg_str, length, NULL); # endif } (void)close(http_fd); continue; } else if (reply == PERMANENT_INCORRECT) { (void)close(http_fd); http_fd = -1; trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to connect() to %s at port %d"), hostname, port); freeaddrinfo(result); return(INCORRECT); } break; /* Success */ } /* Ensure that we succeeded in finding an address. */ if (rp == NULL) { if (errno) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to connect() to %s at port %d : %s"), hostname, port, strerror(errno)); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to connect() to %s at port %d"), hostname, port); } (void)close(http_fd); http_fd = -1; freeaddrinfo(result); return(INCORRECT); } # ifdef WITH_IP_DB if ((ip_from_db == NO) && (use_ip_db == YES) && (get_store_ip() == YES)) { char ip_str[MAX_AFD_INET_ADDRSTRLEN]; if (rp->ai_family == AF_INET) { struct sockaddr_in *sa = (struct sockaddr_in *)rp->ai_addr; (void)my_strncpy(ip_str, inet_ntoa(sa->sin_addr), MAX_AFD_INET_ADDRSTRLEN); } else if (rp->ai_family == AF_INET6) { struct sockaddr_in6 *sa = (struct sockaddr_in6 *)rp->ai_addr; if (inet_ntop(rp->ai_family, &(sa->sin6_addr), ip_str, MAX_AFD_INET_ADDRSTRLEN) == NULL) { ip_str[0] = '\0'; } } else { /* Unknown family type. */ ip_str[0] = '\0'; } if (ip_str[0] != '\0') { add_to_ip_db(hostname, ip_str); } } # endif /* WITH_IP_DB */ freeaddrinfo(result); #else struct sockaddr_in sin; if (http_proxy[0] == '\0') { p_hostname = hostname; } else { p_hostname = http_proxy; } (void)memset((struct sockaddr *) &sin, 0, sizeof(sin)); if ((sin.sin_addr.s_addr = inet_addr(p_hostname)) == -1) { register struct hostent *p_host = NULL; if ((p_host = gethostbyname(p_hostname)) == NULL) { # if !defined (_HPUX) && !defined (_SCO) if (h_errno != 0) { # ifdef WITH_IP_DB char ip_str[MAX_REAL_HOSTNAME_LENGTH + 10]; if (((h_errno == HOST_NOT_FOUND) || # ifdef NO_ADDRESS (h_errno == NO_ADDRESS) || # endif (h_errno == NO_DATA)) && (use_ip_db == YES) && (lookup_ip_from_ip_db(hostname, ip_str, MAX_REAL_HOSTNAME_LENGTH + 10) == SUCCESS)) { if ((sin.sin_addr.s_addr = inet_addr(ip_str)) == -1) { # ifdef LINUX if ((h_errno > 0) && (h_errno < h_nerr)) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to gethostbyname() %s : %s"), ip_str, h_errlist[h_errno]); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to gethostbyname() %s (h_errno = %d) : %s"), ip_str, h_errno, strerror(errno)); } # else trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to gethostbyname() %s (h_errno = %d) : %s"), ip_str, h_errno, strerror(errno)); # endif http_fd = -1; return(INCORRECT); } ip_from_db = YES; } else { # endif /* WITH_IP_DB */ # ifdef LINUX if ((h_errno > 0) && (h_errno < h_nerr)) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to gethostbyname() %s : %s"), hostname, h_errlist[h_errno]); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to gethostbyname() %s (h_errno = %d) : %s"), hostname, h_errno, strerror(errno)); } # else trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to gethostbyname() %s (h_errno = %d) : %s"), hostname, h_errno, strerror(errno)); # endif # ifdef WITH_IP_DB } # endif } else { # endif /* !_HPUX && !_SCO */ trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to gethostbyname() %s : %s"), p_hostname, strerror(errno)); # if !defined (_HPUX) && !defined (_SCO) } # endif http_fd = -1; return(INCORRECT); } /* Copy IP number to socket structure. */ memcpy((char *)&sin.sin_addr, p_host->h_addr, p_host->h_length); } if ((http_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("socket() error : %s"), strerror(errno)); return(INCORRECT); } sin.sin_family = AF_INET; sin.sin_port = htons((u_short)port); if (sndbuf_size > 0) { if (setsockopt(http_fd, SOL_SOCKET, SO_SNDBUF, (char *)&sndbuf_size, sizeof(sndbuf_size)) < 0) { trans_log(WARN_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("setsockopt() error : %s"), strerror(errno)); } hmr.sndbuf_size = sndbuf_size; } else { hmr.sndbuf_size = 0; } if (rcvbuf_size > 0) { if (setsockopt(http_fd, SOL_SOCKET, SO_RCVBUF, (char *)&rcvbuf_size, sizeof(rcvbuf_size)) < 0) { trans_log(WARN_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("setsockopt() error : %s"), strerror(errno)); } hmr.rcvbuf_size = rcvbuf_size; } else { hmr.rcvbuf_size = 0; } # ifdef FTP_CTRL_KEEP_ALIVE_INTERVAL if (timeout_flag != OFF) { reply = 1; if (setsockopt(http_fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&reply, sizeof(reply)) < 0) { trans_log(WARN_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("setsockopt() SO_KEEPALIVE error : %s"), strerror(errno)); } # ifdef TCP_KEEPALIVE reply = timeout_flag; if (setsockopt(http_fd, IPPROTO_IP, TCP_KEEPALIVE, (char *)&reply, sizeof(reply)) < 0) { trans_log(WARN_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("setsockopt() TCP_KEEPALIVE error : %s"), strerror(errno)); } # endif timeout_flag = OFF; } # endif if (connect_with_timeout(http_fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { if (errno) { # ifdef ETIMEDOUT if (errno == ETIMEDOUT) { timeout_flag = ON; } # ifdef ECONNREFUSED else if (errno == ECONNREFUSED) { timeout_flag = CON_REFUSED; } # endif # else # ifdef ECONNREFUSED if (errno == ECONNREFUSED) { timeout_flag = CON_REFUSED; } # endif # endif trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to connect() to %s at port %d : %s"), p_hostname, port, strerror(errno)); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to connect() to %s at port %d"), p_hostname, port); } (void)close(http_fd); http_fd = -1; return(INCORRECT); } # ifdef WITH_IP_DB if ((ip_from_db == NO) && (use_ip_db == YES) && (get_store_ip() == YES)) { char ip_str[MAX_AFD_INET_ADDRSTRLEN]; (void)my_strncpy(ip_str, inet_ntoa(sin.sin_addr), MAX_AFD_INET_ADDRSTRLEN); if (ip_str[0] != '\0') { add_to_ip_db(hostname, ip_str); } } # endif #endif #ifdef WITH_TRACE if (http_proxy[0] == '\0') { length = snprintf(msg_str, MAX_RET_MSG_LENGTH, "Connected to %s at port %d", p_hostname, port); } else { length = snprintf(msg_str, MAX_RET_MSG_LENGTH, "Connected to HTTP proxy %s at port %d", p_hostname, port); } if (length > MAX_RET_MSG_LENGTH) { length = MAX_RET_MSG_LENGTH; } trace_log(NULL, 0, C_TRACE, msg_str, length, NULL); #endif (void)my_strncpy(hmr.hostname, hostname, MAX_REAL_HOSTNAME_LENGTH + 1); (void)my_strncpy(hmr.http_proxy, http_proxy, MAX_REAL_HOSTNAME_LENGTH + 1); (void)my_strncpy(hmr.user, user, MAX_USER_NAME_LENGTH + 1); (void)my_strncpy(hmr.passwd, passwd, MAX_USER_NAME_LENGTH + 1); #ifdef HTTP_NO_AUTO_BASIC_AUTH if (auth_type == AUTH_BASIC) { hmr.www_authenticate = WWW_AUTHENTICATE_BASIC; } else { hmr.www_authenticate = WWW_AUTHENTICATE_UNKNOWN; } #else if (auth_type == AUTH_DIGEST) { hmr.www_authenticate = WWW_AUTHENTICATE_UNKNOWN; } else { hmr.www_authenticate = WWW_AUTHENTICATE_BASIC; } #endif hmr.digest_options = 0; if (init_hmr == YES) { hmr.authorization = NULL; hmr.realm = NULL; hmr.nonce = NULL; hmr.opaque = NULL; } hmr.auth_type = auth_type; #ifdef WITH_SSL hmr.service_type = service; hmr.marker[0] = '\0'; hmr.marker_length = 0; if (list_version == '1') { hmr.listobject_version = '1'; } else { hmr.listobject_version = DEFAULT_AWS4_LIST_VERSION; } if ((service == SERVICE_S3) || (service == SERVICE_NONE)) { /* s3 */ hmr.service[0] = 's'; hmr.service[1] = '3'; hmr.service[2] = '\0'; } else { hmr.service[0] = '\0'; } if (region[0] == '\0') { (void)my_strncpy(hmr.region, DEFAULT_REGION_STR, MAX_REAL_HOSTNAME_LENGTH + 1); } else { (void)my_strncpy(hmr.region, region, MAX_REAL_HOSTNAME_LENGTH + 1); } #endif hmr.port = port; hmr.free = YES; hmr.fra_options = fra_options; hmr.features = features; hmr.http_version = 0; hmr.http_options = 0; hmr.http_options_not_working = 0; hmr.bytes_buffered = 0; hmr.bytes_read = 0; if (init_hmr == YES) { hmr.filename = NULL; } hmr.debug = debug; #ifdef _WITH_EXTRA_CHECK hmr.http_etag[0] = '\0'; hmr.http_weak_etag = YES; #endif #ifdef WITH_SSL hmr.tls_auth = tls_auth; if ((tls_auth == YES) || (tls_auth == BOTH)) { uint64_t ctx_options; char *p_env, *p_env1; if (ssl_ctx != NULL) { SSL_CTX_free(ssl_ctx); } SSLeay_add_ssl_algorithms(); if ((ssl_ctx = (SSL_CTX *)SSL_CTX_new(SSLv23_client_method())) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("SSL_CTX_new() unable to create a new SSL context structure.")); (void)close(http_fd); http_fd = -1; return(INCORRECT); } # ifdef NO_SSLv2 ctx_options = (SSL_OP_ALL | SSL_OP_NO_SSLv2); # else # ifdef NO_SSLv3 ctx_options = (SSL_OP_ALL | SSL_OP_NO_SSLv3); # else # ifdef NO_SSLv23 ctx_options = (SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); # else # ifdef NO_SSLv23TLS1_0 ctx_options = (SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); # else # ifdef NO_SSLv23TLS1_0TLS1_1 ctx_options = (SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); # else ctx_options = SSL_OP_ALL; # endif # endif # endif # endif # endif if (hmr.features & PROT_OPT_TLS_LEGACY_RENEGOTIATION) { ctx_options |= SSL_OP_LEGACY_SERVER_CONNECT; } SSL_CTX_set_options(ssl_ctx, ctx_options); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); if ((p_env = getenv("SSL_CIPHER")) != NULL) { SSL_CTX_set_cipher_list(ssl_ctx, p_env); } else { SSL_CTX_set_cipher_list(ssl_ctx, NULL); } if (((p_env = getenv(X509_get_default_cert_file_env())) != NULL) && ((p_env1 = getenv(X509_get_default_cert_dir_env())) != NULL)) { SSL_CTX_load_verify_locations(ssl_ctx, p_env, p_env1); } else { if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("SSL_CTX_set_default_verify_paths() failed.")); (void)close(http_fd); http_fd = -1; return(INCORRECT); } } SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); ssl_con = (SSL *)SSL_new(ssl_ctx); SSL_set_connect_state(ssl_con); SSL_set_fd(ssl_con, http_fd); if (!SSL_set_tlsext_host_name(ssl_con, hostname)) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("SSL_set_tlsext_host_name() failed to enable ServerNameIndication for %s"), hostname); (void)close(http_fd); http_fd = -1; return(INCORRECT); } /* * NOTE: Because we have set SSL_MODE_AUTO_RETRY, a SSL_read() can * block even when we use select(). The same thing might be true * for SSL_write() but have so far not encountered this case. * It might be cleaner not to set SSL_MODE_AUTO_RETRY and handle * SSL_ERROR_WANT_READ error case. */ if (signal(SIGALRM, sig_handler) == SIG_ERR) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("Failed to set signal handler : %s"), strerror(errno)); (void)close(http_fd); http_fd = -1; return(INCORRECT); } if (sigsetjmp(env_alrm, 1) != 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("SSL_connect() timeout (%ld)"), transfer_timeout); timeout_flag = ON; (void)close(http_fd); http_fd = -1; return(INCORRECT); } (void)alarm(transfer_timeout); reply = SSL_connect(ssl_con); (void)alarm(0); if (reply <= 0) { char *ptr; ptr = ssl_error_msg("SSL_connect", ssl_con, NULL, reply, msg_str); reply = SSL_get_verify_result(ssl_con); if (reply == X509_V_ERR_CRL_SIGNATURE_FAILURE) { (void)my_strncpy(ptr, _(" | Verify result: The signature of the certificate is invalid!"), MAX_RET_MSG_LENGTH - (ptr - msg_str)); } else if (reply == X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD) { (void)my_strncpy(ptr, _(" | Verify result: The CRL nextUpdate field contains an invalid time."), MAX_RET_MSG_LENGTH - (ptr - msg_str)); } else if (reply == X509_V_ERR_CRL_HAS_EXPIRED) { (void)my_strncpy(ptr, _(" | Verify result: The CRL has expired."), MAX_RET_MSG_LENGTH - (ptr - msg_str)); } else if (reply == X509_V_ERR_CERT_REVOKED) { (void)my_strncpy(ptr, _(" | Verify result: Certificate revoked."), MAX_RET_MSG_LENGTH - (ptr - msg_str)); } else if (reply > X509_V_OK) { (void)snprintf(ptr, MAX_RET_MSG_LENGTH - (ptr - msg_str), _(" | Verify result: %d"), reply); } reply = INCORRECT; SSL_free(ssl_con); ssl_con = NULL; (void)close(http_fd); http_fd = -1; } else { # ifdef WITH_TRACE const char *ssl_version; int length; const SSL_CIPHER *ssl_cipher; ssl_version = SSL_get_cipher_version(ssl_con); length = strlen(msg_str); if ((ssl_cipher = SSL_get_current_cipher(ssl_con)) != NULL) { int ssl_bits; SSL_CIPHER_get_bits(ssl_cipher, &ssl_bits); (void)snprintf(&msg_str[length], MAX_RET_MSG_LENGTH - length, " <%s, cipher %s, %d bits>", ssl_version, SSL_CIPHER_get_name(ssl_cipher), ssl_bits); } else { (void)snprintf(&msg_str[length], MAX_RET_MSG_LENGTH - length, " <%s, cipher ?, ? bits>", ssl_version); } # endif if (hmr.features & PROT_OPT_TLS_STRICT_VERIFY) { X509 *cert; if ((cert = SSL_get_peer_certificate(ssl_con)) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_connect", NULL, _("No certificate presented by %s. Strict TLS requested."), hostname); (void)SSL_shutdown(ssl_con); SSL_free(ssl_con); ssl_con = NULL; (void)close(http_fd); http_fd = -1; X509_free(cert); return(INCORRECT); } else { char *issuer = NULL; # ifdef WITH_TRACE char *subject; issuer = rfc2253_formatted(X509_get_issuer_name(cert)); subject = rfc2253_formatted(X509_get_subject_name(cert)); trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_connect", NULL, "", subject, issuer); free(subject); # endif if ((reply = SSL_get_verify_result(ssl_con)) != X509_V_OK) { switch (reply) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: if (issuer == NULL) { issuer = rfc2253_formatted(X509_get_issuer_name(cert)); } (void)snprintf(msg_str, MAX_RET_MSG_LENGTH, "Unable to locally verify the issuer's (%s) authority.", issuer); break; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: (void)snprintf(msg_str, MAX_RET_MSG_LENGTH, "Self-signed certificate encountered."); case X509_V_ERR_CERT_NOT_YET_VALID: (void)snprintf(msg_str, MAX_RET_MSG_LENGTH, "Issued certificate not yet valid."); break; case X509_V_ERR_CERT_HAS_EXPIRED: (void)snprintf(msg_str, MAX_RET_MSG_LENGTH, "Issued certificate has expired."); break; default: (void)snprintf(msg_str, MAX_RET_MSG_LENGTH, "%s", X509_verify_cert_error_string(reply)); } (void)SSL_shutdown(ssl_con); SSL_free(ssl_con); ssl_con = NULL; (void)close(http_fd); http_fd = -1; free(issuer); X509_free(cert); return(INCORRECT); } free(issuer); } X509_free(cert); } reply = SUCCESS; } # ifdef WITH_SSL_READ_AHEAD /* This is not set because I could not detect any advantage using this. */ SSL_set_read_ahead(ssl_con, 1); # endif return(reply); } #endif /* WITH_SSL */ } return(SUCCESS); } /*########################### http_version() ############################*/ int http_version(void) { return(hmr.http_version); } /*########################## http_set_marker() ##########################*/ void http_set_marker(char *marker, int marker_length) { (void)strcpy(hmr.marker, marker); hmr.marker_length = marker_length; return; } /*##################### http_reset_authentication() #####################*/ void http_reset_authentication(int auth_type) { if (hmr.authorization != NULL) { free(hmr.authorization); hmr.authorization = NULL; } #ifdef HTTP_NO_AUTO_BASIC_AUTH if (auth_type == AUTH_BASIC) { hmr.www_authenticate = WWW_AUTHENTICATE_BASIC; } else { hmr.www_authenticate = WWW_AUTHENTICATE_UNKNOWN; } #else if (auth_type == AUTH_DIGEST) { hmr.www_authenticate = WWW_AUTHENTICATE_UNKNOWN; } else { hmr.www_authenticate = WWW_AUTHENTICATE_BASIC; } #endif return; } /*############################## http_get() #############################*/ int http_get(char *path, char *filename, char *new_filename, #ifdef _WITH_EXTRA_CHECK char *etag, #endif off_t *content_length, off_t offset) { int reply; if (http_fd == -1) { reply = PERMANENT_DISCONNECT; } else { int resource_length; char accept_encoding[24], *p_path = path, range[13 + MAX_OFF_T_LENGTH + 4], #ifdef _WITH_EXTRA_CHECK none_match[16 + MAX_EXTRA_LS_DATA_LENGTH + 5], #endif #ifdef WITH_SSL tmp_path[MAX_REAL_HOSTNAME_LENGTH], #endif resource[8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + MAX_AWS4_PARAMETER_LENGTH + MAX_INT_LENGTH + MAX_FILENAME_LENGTH + 1]; #ifdef WITH_SSL if (((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) && (filename[0] != '\0')) { resource_length = 0; if (hmr.features & BUCKETNAME_IS_IN_PATH) { while ((resource_length < MAX_RECIPIENT_LENGTH) && (path[resource_length] != '/') && (path[resource_length] != '\0')) { tmp_path[resource_length] = path[resource_length]; resource_length++; } if (path[resource_length] == '/') { tmp_path[resource_length] = path[resource_length]; resource_length++; } } tmp_path[resource_length] = '\0'; p_path = tmp_path; } #endif hmr.bytes_read = 0; hmr.retries = 0; hmr.date = -1; if (hmr.filename != NULL) { hmr.filename[0] = '\0'; } if (hmr.http_proxy[0] == '\0') { if (filename[0] == '/') { resource_length = snprintf(resource, MAX_FILENAME_LENGTH + 1, "%s", filename); } else { char *tmp_ptr; if (*p_path == '/') { resource_length = 0; tmp_ptr = resource; } else { resource_length = 1; resource[0] = '/'; tmp_ptr = &resource[1]; } resource_length += snprintf(tmp_ptr, 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "%s%s", p_path, filename); } } else { #ifdef WITH_SSL if (hmr.tls_auth == YES) { if (filename[0] == '/') { resource_length = snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_FILENAME_LENGTH + 1, "https://%s%s", hmr.hostname, filename); } else { if (*p_path == '/') { resource_length = snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s%s%s", hmr.hostname, p_path, filename); } else { resource_length = snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s/%s%s", hmr.hostname, p_path, filename); } } } else { #endif if (filename[0] == '/') { resource_length = snprintf(resource, 7 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_FILENAME_LENGTH + 1, "http://%s%s", hmr.hostname, filename); } else { if (*p_path == '/') { resource_length = snprintf(resource, 7 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 16 + 1, "http://%s%s%s", hmr.hostname, p_path, filename); } else { resource_length = snprintf(resource, 7 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 16 + 1, "http://%s/%s%s", hmr.hostname, p_path, filename); } } #ifdef WITH_SSL } #endif } if (filename[0] != '\0') { char *ptr = filename + strlen(filename) - 1; if ((*ptr == 'z') && (*(ptr - 1) == 'g') && (*(ptr - 2) == '.')) { /* \r\nAccept-Encoding: gzip */ accept_encoding[0] = '\r'; accept_encoding[1] = '\n'; accept_encoding[2] = 'A'; accept_encoding[3] = 'c'; accept_encoding[4] = 'c'; accept_encoding[5] = 'e'; accept_encoding[6] = 'p'; accept_encoding[7] = 't'; accept_encoding[8] = '-'; accept_encoding[9] = 'E'; accept_encoding[10] = 'n'; accept_encoding[11] = 'c'; accept_encoding[12] = 'o'; accept_encoding[13] = 'd'; accept_encoding[14] = 'i'; accept_encoding[15] = 'n'; accept_encoding[16] = 'g'; accept_encoding[17] = ':'; accept_encoding[18] = ' '; accept_encoding[19] = 'g'; accept_encoding[20] = 'z'; accept_encoding[21] = 'i'; accept_encoding[22] = 'p'; accept_encoding[23] = '\0'; } else { accept_encoding[0] = '\0'; } } else { accept_encoding[0] = '\0'; } retry_get_range: if ((offset == 0) || (offset < 0)) { range[0] = '\0'; } else { (void)snprintf(range, 13 + MAX_OFF_T_LENGTH + 4, #if SIZEOF_OFF_T == 4 "Range: bytes=%ld-\r\n", #else "Range: bytes=%lld-\r\n", #endif (pri_off_t)offset); } #ifdef _WITH_EXTRA_CHECK if (etag[0] == '\0') { none_match[0] = '\0'; } else { size_t bytes_copied; /* If-None-Match */ none_match[0] = 'I'; none_match[1] = 'f'; none_match[2] = '-'; none_match[3] = 'N'; none_match[4] = 'o'; none_match[5] = 'n'; none_match[6] = 'e'; none_match[7] = '-'; none_match[8] = 'M'; none_match[9] = 'a'; none_match[10] = 't'; none_match[11] = 'c'; none_match[12] = 'h'; none_match[13] = ':'; none_match[14] = ' '; none_match[15] = '"'; bytes_copied = my_strlcpy(&none_match[16], etag, MAX_EXTRA_LS_DATA_LENGTH); none_match[16 + bytes_copied] = '"'; none_match[16 + bytes_copied + 1] = '\r'; none_match[16 + bytes_copied + 2] = '\n'; none_match[16 + bytes_copied + 3] = '\0'; } #endif #ifdef WITH_SSL if ((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) { /* Directory listing? */ if (filename[0] == '\0') { if (prepare_aws4_listing(filename, p_path, resource, &resource_length) != SUCCESS) { return(INCORRECT); } } else { if (aws_cmd("GET", filename, p_path, "", &hmr) != SUCCESS) { return(INCORRECT); } } } else { #endif if (hmr.authorization == NULL) { if (hmr.www_authenticate == WWW_AUTHENTICATE_BASIC) { if (basic_authentication(&hmr) != SUCCESS) { return(INCORRECT); } } } else { if ((hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256_S)) { if (digest_authentication("GET", path, filename, &hmr) != SUCCESS) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_get", NULL, _("Failed to create new digest authentication.")); return(INCORRECT); } } } #ifdef WITH_SSL } #endif retry_get: #ifdef _WITH_EXTRA_CHECK if ((reply = command(http_fd, "GET %s HTTP/1.1\r\nHost: %s\r\n%sUser-Agent: AFD/%s\r\n%s%sContent-length: 0\r\nAccept: */*%s\r\n", resource, hmr.hostname, range, PACKAGE_VERSION, (hmr.authorization == NULL) ? "" : hmr.authorization, none_match, accept_encoding)) == SUCCESS) #else if ((reply = command(http_fd, "GET %s HTTP/1.1\r\nHost: %s\r\n%sUser-Agent: AFD/%s\r\n%sContent-length: 0\r\nAccept: */*%s\r\n", resource, hmr.hostname, range, PACKAGE_VERSION, (hmr.authorization == NULL) ? "" : hmr.authorization, accept_encoding)) == SUCCESS) #endif { hmr.content_length = -1; if (((reply = get_http_reply(&hmr.bytes_buffered, 200, __LINE__)) == 200) || (reply == 204) || /* No content. */ (reply == 206)) /* Partial Content. */ { if ((filename[0] != '\0') && (hmr.content_length == 0)) { reply = NOTHING_TO_FETCH; } else { if (hmr.chunked == YES) { reply = CHUNKED; } else { reply = SUCCESS; } } if (*content_length != hmr.content_length) { *content_length = hmr.content_length; } if (new_filename != NULL) { if ((hmr.filename == NULL) || (hmr.filename[0] == '\0')) { new_filename[0] = '\0'; } else { (void)strcpy(new_filename, hmr.filename); } } #ifdef _WITH_EXTRA_CHECK (void)strcpy(etag, hmr.http_etag); #endif } else if (reply == 401) /* Unauthorized */ { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; if (new_filename != NULL) { new_filename[0] = '\0'; } if (init_authentication("GET", path, filename) == SUCCESS) { if (check_connection() > INCORRECT) { goto retry_get; } } } else if (reply == 304) /* Not Modified */ { if (new_filename != NULL) { new_filename[0] = '\0'; } reply = NOTHING_TO_FETCH; } else if ((reply == 403) || /* Forbidden */ (reply == 404)) /* Not Found */ { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; if (new_filename != NULL) { new_filename[0] = '\0'; } } else if (reply == 416) /* Requested Range Not Satisfiable */ { offset = 0; goto retry_get_range; } else if (reply == CONNECTION_REOPENED) { goto retry_get; } else { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; if (new_filename != NULL) { new_filename[0] = '\0'; } } } else if ((reply == INCORRECT) && (errno == EPIPE)) { /* * This is an indication that the remote server has * closed the connection, so lets try and reopen it. */ if (hmr.debug > 0) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_get", NULL, "command() returned INCORRECT and errno is EPIPE, retrying connection."); } if (check_connection() > INCORRECT) { goto retry_get; } } if (((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) && (hmr.authorization != NULL)) { free(hmr.authorization); hmr.authorization = NULL; } } return(reply); } #ifdef WITH_SSL /*####################### prepare_aws4_listing() ########################*/ static int prepare_aws4_listing(char *filename, char *path, char *resource, int *resource_length) { int i = 0; size_t size; char bucket_name_only[MAX_PATH_LENGTH], *canonical_query = NULL, *p_path; p_path = path; if (hmr.features & BUCKETNAME_IS_IN_PATH) { /* Remove bucket name */ while ((*p_path != '/') && (*p_path != '\0')) { bucket_name_only[i] = *p_path; i++; p_path++; } if (*p_path == '/') { bucket_name_only[i] = '/'; bucket_name_only[i + 1] = '\0'; if (*p_path != '\0') { p_path++; } if (hmr.http_proxy[0] == '\0') { char *tmp_ptr; if (*path == '/') { *resource_length = 0; tmp_ptr = resource; } else { *resource_length = 1; resource[0] = '/'; tmp_ptr = &resource[1]; } *resource_length += snprintf(tmp_ptr, 1 + MAX_RECIPIENT_LENGTH + 1, "%s", bucket_name_only); } else { if (*path == '/') { *resource_length = snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + 1, "%s://%s%s", (hmr.tls_auth == YES) ? "https" : "http", hmr.hostname, bucket_name_only); } else { *resource_length = snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + 1, "%s://%s/%s", (hmr.tls_auth == YES) ? "https" : "http", hmr.hostname, bucket_name_only); } } } else { bucket_name_only[i] = '\0'; } } else { bucket_name_only[0] = '\0'; } if ((*p_path == '\0') || ((*p_path == '/') && (*(p_path + 1) == '\0'))) { size = MAX_AWS4_PARAMETER_LENGTH + MAX_INT_LENGTH + (hmr.marker_length * 3) + 1 + 1; if ((canonical_query = malloc(size)) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "prepare_aws4_listing", NULL, "Failed to malloc() for canonical query : %s", strerror(errno)); return(INCORRECT); } if (hmr.listobject_version == '1') { /* Prepare CanonicalQueryString */ /* NOTE: This MUST be sorted alphabetically by key name! */ if (hmr.marker[0] == '\0') { i = snprintf(canonical_query, size, "%smax-keys=%d", (hmr.fra_options & NO_DELIMITER) ? "" : "delimiter=%2F&", AWS4_MAX_KEYS); } else { char *marker_encoded; if ((marker_encoded = malloc(((hmr.marker_length * 3) + 1))) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "prepare_aws4_listing", NULL, "Failed to malloc() for marker : %s", strerror(errno)); free(canonical_query); return(INCORRECT); } url_encode(hmr.marker, marker_encoded); i = snprintf(canonical_query, size, "%smarker=%s&max-keys=%d", (hmr.fra_options & NO_DELIMITER) ? "" : "delimiter=%2F&", marker_encoded, AWS4_MAX_KEYS); free(marker_encoded); } } else { /* Prepare CanonicalQueryString */ /* NOTE: This MUST be sorted alphabetically by key name! */ if (hmr.marker[0] == '\0') { i = snprintf(canonical_query, size, "%slist-type=2&max-keys=%d", (hmr.fra_options & NO_DELIMITER) ? "" : "delimiter=%2F&", AWS4_MAX_KEYS); } else { char *marker_encoded; if ((marker_encoded = malloc(((hmr.marker_length * 3) + 1))) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "prepare_aws4_listing", NULL, "Failed to malloc() for marker : %s", strerror(errno)); free(canonical_query); return(INCORRECT); } url_encode(hmr.marker, marker_encoded); i = snprintf(canonical_query, size, "continuation-token=%s&%slist-type=2&max-keys=%d", marker_encoded, (hmr.fra_options & NO_DELIMITER) ? "" : "delimiter=%2F&", AWS4_MAX_KEYS); free(marker_encoded); } } } else { size_t prefix_size; char *prefix_encoded; prefix_size = (strlen(p_path) * 3) + 1; if ((prefix_encoded = malloc(prefix_size)) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "prepare_aws4_listing", NULL, "Failed to malloc() for prefix_encoded : %s", strerror(errno)); return(INCORRECT); } url_encode(p_path, prefix_encoded); size = MAX_AWS4_PARAMETER_LENGTH + MAX_INT_LENGTH + (hmr.marker_length * 3) + 1 + prefix_size + 1 + 1; if ((canonical_query = malloc(size)) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "prepare_aws4_listing", NULL, "Failed to malloc() for canonical query : %s", strerror(errno)); free(prefix_encoded); return(INCORRECT); } if (hmr.listobject_version == '1') { /* Prepare CanonicalQueryString */ /* NOTE: This MUST be sorted alphabetically by key name! */ if (hmr.marker[0] == '\0') { i = snprintf(canonical_query, size, "%smax-keys=%d&prefix=%s", (hmr.fra_options & NO_DELIMITER) ? "" : "delimiter=%2F&", AWS4_MAX_KEYS, prefix_encoded); } else { char *marker_encoded; if ((marker_encoded = malloc(((hmr.marker_length * 3) + 1))) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "prepare_aws4_listing", NULL, "Failed to malloc() for marker : %s", strerror(errno)); free(prefix_encoded); free(canonical_query); return(INCORRECT); } url_encode(hmr.marker, marker_encoded); i = snprintf(canonical_query, size, "%smarker=%s&max-keys=%d&prefix=%s", (hmr.fra_options & NO_DELIMITER) ? "" : "delimiter=%2F&", marker_encoded, AWS4_MAX_KEYS, prefix_encoded); free(marker_encoded); } } else { /* Prepare CanonicalQueryString */ /* NOTE: This MUST be sorted alphabetically by key name! */ if (hmr.marker[0] == '\0') { i = snprintf(canonical_query, size, "%slist-type=2&max-keys=%d&prefix=%s", (hmr.fra_options & NO_DELIMITER) ? "" : "delimiter=%2F&", AWS4_MAX_KEYS, prefix_encoded); } else { char *marker_encoded; if ((marker_encoded = malloc(((hmr.marker_length * 3) + 1))) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "prepare_aws4_listing", NULL, "Failed to malloc() for marker : %s", strerror(errno)); free(prefix_encoded); free(canonical_query); return(INCORRECT); } url_encode(hmr.marker, marker_encoded); i = snprintf(canonical_query, size, "continuation-token=%s&%slist-type=2&max-keys=%d&prefix=%s", marker_encoded, (hmr.fra_options & NO_DELIMITER) ? "" : "delimiter=%2F&", AWS4_MAX_KEYS, prefix_encoded); free(marker_encoded); } } free(prefix_encoded); } if (i >= size) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "prepare_aws4_listing", NULL, #if SIZEOF_SIZE_T == 4 "Overflow in canonical_query detected (%d >= %d)", #else "Overflow in canonical_query detected (%d >= %lld)", #endif i, (pri_size_t)size); free(canonical_query); return(INCORRECT); } resource[*resource_length] = '?'; (void)strcpy(&resource[*resource_length + 1], canonical_query); if (my_strlcpy(&resource[*resource_length + 1], canonical_query, (8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + MAX_AWS4_PARAMETER_LENGTH + MAX_INT_LENGTH + MAX_FILENAME_LENGTH + 1) - (*resource_length + 1)) >= ((8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + MAX_AWS4_PARAMETER_LENGTH + MAX_INT_LENGTH + MAX_FILENAME_LENGTH + 1) - (*resource_length + 1))) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "prepare_aws4_listing", NULL, "Unable to append canonical query because resource is not large enough."); free(canonical_query); return(INCORRECT); } if (aws_cmd("GET", filename, bucket_name_only, canonical_query, &hmr) != SUCCESS) { free(canonical_query); return(INCORRECT); } free(canonical_query); return(SUCCESS); } #endif /* WITH_SSL */ /*############################## http_put() #############################*/ int http_put(char *path, char *filename, char *fullname, off_t file_size, char *file_content_hash_hex, int first_file) { char content_type[MAX_CONTENT_TYPE_LENGTH]; if (http_fd == -1) { return(PERMANENT_DISCONNECT); } hmr.retries = 0; hmr.date = -1; if (first_file == NO) { int reply; if ((reply = check_connection()) == CONNECTION_REOPENED) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_put", NULL, _("Reconnected.")); } else if (reply == INCORRECT) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_put", NULL, _("Failed to reconnect.")); return(INCORRECT); } } #ifdef WITH_SSL if (hmr.auth_type == AUTH_AWS4_HMAC_SHA256) { if (aws4_put_authentication(filename, fullname, file_size, path, file_content_hash_hex, &hmr) != SUCCESS) { return(INCORRECT); } } else { #endif if (hmr.authorization == NULL) { if (hmr.www_authenticate == WWW_AUTHENTICATE_BASIC) { if (basic_authentication(&hmr) != SUCCESS) { return(INCORRECT); } } else { if ((hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256_S)) { if (digest_authentication("PUT", path, filename, &hmr) != SUCCESS) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_put", NULL, _("Failed to create new digest authentication.")); return(INCORRECT); } } } } #ifdef WITH_SSL } #endif get_content_type(filename, content_type, YES); if (((hmr.features & PROT_OPT_NO_EXPECT) == 0) && (file_size > 0)) { int reply; retry_put: if ((reply = command(http_fd, #if SIZEOF_OFF_T == 4 "PUT %s%s%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: AFD/%s\r\nAccept: */*\r\nContent-length: %ld\r\n%sContent-Type: %s\r\n%sControl: overwrite=1\r\n", #else "PUT %s%s%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: AFD/%s\r\nAccept: */*\r\nContent-length: %lld\r\n%sContent-Type: %s\r\n%sControl: overwrite=1\r\n", #endif (*path != '/') ? "/" : "", path, filename, hmr.hostname, PACKAGE_VERSION, (pri_off_t)file_size, (hmr.authorization == NULL) ? "" : hmr.authorization, content_type, "Expect: 100-continue\r\n")) == SUCCESS) { if (((reply = get_http_reply(NULL, 100, __LINE__)) == 100) || (reply == 200)) { reply = SUCCESS; } else if (reply == 401) /* Unauthorized */ { clear_msg_str(); if (init_authentication("PUT", path, filename) == SUCCESS) { if (check_connection() > INCORRECT) { goto retry_put; } } } else if (reply == CONNECTION_REOPENED) { goto retry_put; } else { clear_msg_str(); } } hmr.bytes_buffered = 0; hmr.bytes_read = 0; if ((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) && (hmr.authorization != NULL)) { free(hmr.authorization); hmr.authorization = NULL; } return(reply); } else { return(command(http_fd, #if SIZEOF_OFF_T == 4 "PUT %s%s%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: AFD/%s\r\nAccept: */*\r\nContent-length: %ld\r\n%sContent-Type: %s\r\nControl: overwrite=1\r\n", #else "PUT %s%s%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: AFD/%s\r\nAccept: */*\r\nContent-length: %lld\r\n%sContent-Type: %s\r\nControl: overwrite=1\r\n", #endif (*path != '/') ? "/" : "", path, filename, hmr.hostname, PACKAGE_VERSION, (pri_off_t)file_size, (hmr.authorization == NULL) ? "" : hmr.authorization, content_type)); } } /*######################### http_put_response() #########################*/ int http_put_response(void) { int reply; hmr.retries = -1; /* -1 so we do not do a reconnect in get_http_reply()! */ hmr.date = -1; hmr.content_length = 0; if ((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) && (hmr.authorization != NULL)) { free(hmr.authorization); hmr.authorization = NULL; } if (((reply = get_http_reply(NULL, 201, __LINE__)) == 201) || (reply == 204) || (reply == 200)) { reply = SUCCESS; } else { clear_msg_str(); } hmr.bytes_buffered = 0; hmr.bytes_read = 0; if ((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) && (hmr.authorization != NULL)) { free(hmr.authorization); hmr.authorization = NULL; } return(reply); } /*############################## http_del() #############################*/ int http_del(char *path, char *filename) { int reply; if (http_fd == -1) { reply = PERMANENT_DISCONNECT; } else { char resource[8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1]; hmr.retries = 0; hmr.date = -1; if (hmr.http_proxy[0] == '\0') { if (filename[0] == '/') { (void)strcpy(resource, filename); } else { if (*path == '/') { (void)snprintf(resource, MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "%s%s", path, filename); } else { (void)snprintf(resource, 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "/%s%s", path, filename); } } } else { #ifdef WITH_SSL if (hmr.tls_auth == YES) { if (filename[0] == '/') { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s%s", hmr.hostname, filename); } else { if (*path == '/') { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s%s%s", hmr.hostname, path, filename); } else { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s/%s%s", hmr.hostname, path, filename); } } } else { #endif if (filename[0] == '/') { (void)snprintf(resource, 7 + MAX_REAL_HOSTNAME_LENGTH + MAX_FILENAME_LENGTH + 1, "http://%s%s", hmr.hostname, filename); } else { if (*path == '/') { (void)snprintf(resource, 7 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "http://%s%s%s", hmr.hostname, path, filename); } else { (void)snprintf(resource, 7 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "http://%s/%s%s", hmr.hostname, path, filename); } } #ifdef WITH_SSL } #endif } #ifdef WITH_SSL if ((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) { if (aws_cmd("DELETE", filename, path, "", &hmr) != SUCCESS) { return(INCORRECT); } } else { #endif if (hmr.authorization == NULL) { if (hmr.www_authenticate == WWW_AUTHENTICATE_BASIC) { if (basic_authentication(&hmr) != SUCCESS) { return(INCORRECT); } } } else { if ((hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256_S)) { if (digest_authentication("DELETE", path, filename, &hmr) != SUCCESS) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_delete", NULL, _("Failed to create new digest authentication.")); return(INCORRECT); } } } #ifdef WITH_SSL } #endif retry_del: if ((reply = command(http_fd, "DELETE %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: AFD/%s\r\n%sContent-length: 0\r\n", resource, hmr.hostname, PACKAGE_VERSION, (hmr.authorization == NULL) ? "" : hmr.authorization)) == SUCCESS) { if (((reply = get_http_reply(NULL, 200, __LINE__)) == 200) || (reply == 204)) { reply = SUCCESS; } else if (reply == 401) /* Unauthorized */ { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; if (init_authentication("DELETE", path, filename) == SUCCESS) { if (check_connection() > INCORRECT) { goto retry_del; } } } else if (reply == CONNECTION_REOPENED) { goto retry_del; } else { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; } } if (((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) && (hmr.authorization != NULL)) { free(hmr.authorization); hmr.authorization = NULL; } } return(reply); } /*############################ http_options() ###########################*/ int http_options(char *path) { int reply; if (http_fd == -1) { reply = PERMANENT_DISCONNECT; } else { char resource[8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1]; if (hmr.http_options_not_working & HTTP_OPTION_OPTIONS) { return(SUCCESS); } hmr.retries = 0; hmr.date = -1; if (*path == '\0') { resource[0] = '*'; resource[1] = '\0'; } else { #ifdef WITH_SSL if (hmr.tls_auth == YES) { if (*path == '/') { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s%s", hmr.hostname, path); } else { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s/%s", hmr.hostname, path); } } else { #endif if (*path == '/') { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "http://%s%s", hmr.hostname, path); } else { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "http://%s/%s", hmr.hostname, path); } #ifdef WITH_SSL } #endif } #ifdef WITH_SSL if ((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) { if (aws_cmd("OPTIONS", "", path, "", &hmr) != SUCCESS) { return(INCORRECT); } } else { #endif if (hmr.authorization == NULL) { if (hmr.www_authenticate == WWW_AUTHENTICATE_BASIC) { if (basic_authentication(&hmr) != SUCCESS) { return(INCORRECT); } } } else { if ((hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256_S)) { if (digest_authentication("OPTIONS", path, "", &hmr) != SUCCESS) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_options", NULL, _("Failed to create new digest authentication.")); return(INCORRECT); } } } #ifdef WITH_SSL } #endif retry_options: if ((reply = command(http_fd, "OPTIONS %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: AFD/%s\r\n%sContent-length: 0\r\nAccept: */*\r\n", resource, hmr.hostname, PACKAGE_VERSION, (hmr.authorization == NULL) ? "" : hmr.authorization)) == SUCCESS) { if ((reply = get_http_reply(&hmr.bytes_buffered, 200, __LINE__)) == 200) { reply = SUCCESS; if (hmr.chunked == YES) { read_last_chunk(); } } else if (reply == 401) /* Unauthorized */ { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; if (init_authentication("OPTIONS", path, "") == SUCCESS) { if (check_connection() > INCORRECT) { goto retry_options; } } } else if ((reply == 403) || /* Forbidden */ (reply == 405) || /* Not Allowed */ (reply == 500)) /* Internal Server Error */ { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; reply = SUCCESS; hmr.http_options_not_working |= HTTP_OPTION_OPTIONS; } else if (reply == CONNECTION_REOPENED) { goto retry_options; } else { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; } } if (((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) && (hmr.authorization != NULL)) { free(hmr.authorization); hmr.authorization = NULL; } } return(reply); } /*############################# http_head() #############################*/ int http_head(char *path, char *filename, off_t *content_length, time_t *date) { int reply; if (http_fd == -1) { reply = PERMANENT_DISCONNECT; } else { char resource[8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1]; if (hmr.http_options_not_working & HTTP_OPTION_HEAD) { hmr.date = 0; hmr.content_length = 0; *content_length = -1; if (date != NULL) { *date = 0; } return(SUCCESS); } hmr.retries = 0; hmr.date = 0; if (hmr.http_proxy[0] == '\0') { /* Check if filename already contains path. */ if (filename[0] == '/') { (void)strcpy(resource, filename); } else { if (*path == '/') { (void)snprintf(resource, MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "%s%s", path, filename); } else { (void)snprintf(resource, 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "/%s%s", path, filename); } } } else { #ifdef WITH_SSL if (hmr.tls_auth == YES) { /* Check if filename already contains path. */ if (filename[0] == '/') { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s%s", hmr.hostname, filename); } else { if (*path == '/') { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s%s%s", hmr.hostname, path, filename); } else { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "https://%s/%s%s", hmr.hostname, path, filename); } } } else { #endif /* Check if filename already contains path. */ if (filename[0] == '/') { (void)snprintf(resource, 7 + MAX_REAL_HOSTNAME_LENGTH + MAX_FILENAME_LENGTH + 1, "http://%s%s", hmr.hostname, filename); } else { if (*path == '/') { (void)snprintf(resource, 7 + MAX_REAL_HOSTNAME_LENGTH + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "http://%s%s%s", hmr.hostname, path, filename); } else { (void)snprintf(resource, 7 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + MAX_FILENAME_LENGTH + 1, "http://%s/%s%s", hmr.hostname, path, filename); } } #ifdef WITH_SSL } #endif } #ifdef WITH_SSL if ((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) { if (aws_cmd("HEAD", filename, path, "", &hmr) != SUCCESS) { return(INCORRECT); } } else { #endif if (hmr.authorization == NULL) { if (hmr.www_authenticate == WWW_AUTHENTICATE_BASIC) { if (basic_authentication(&hmr) != SUCCESS) { return(INCORRECT); } } } else { if ((hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256_S)) { if (digest_authentication("HEAD", path, filename, &hmr) != SUCCESS) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_head", NULL, _("Failed to create new digest authentication.")); return(INCORRECT); } } } #ifdef WITH_SSL } #endif retry_head: if ((reply = command(http_fd, "HEAD %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: AFD/%s\r\n%sContent-length: 0\r\nAccept: */*\r\n", resource, hmr.hostname, PACKAGE_VERSION, (hmr.authorization == NULL) ? "" : hmr.authorization)) == SUCCESS) { if (((reply = get_http_reply(NULL, 999, __LINE__)) == 200) || (reply == 204) || /* No content. */ (reply == 206)) /* Partial Content. */ { reply = SUCCESS; *content_length = hmr.content_length; if (date != NULL) { *date = hmr.date; } } else if (reply == 401) /* Unauthorized */ { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; if (init_authentication("HEAD", path, filename) == SUCCESS) { if (check_connection() > INCORRECT) { goto retry_head; } } } else if ((reply == 400) || /* Bad Request */ (reply == 405) || /* Method Not Allowed */ (reply == 403) || /* Forbidden */ (reply == 501)) /* Not Implemented */ { hmr.http_options_not_working |= HTTP_OPTION_HEAD; *content_length = -1; if (date != NULL) { *date = 0; } hmr.bytes_buffered = 0; hmr.bytes_read = 0; } else if (reply == CONNECTION_REOPENED) { goto retry_head; } else { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; } } if (((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) && (hmr.authorization != NULL)) { free(hmr.authorization); hmr.authorization = NULL; } } return(reply); } /*############################# http_write() ############################*/ int http_write(char *block, char *buffer, int size) { char *ptr = block; int status; fd_set wset; /* Initialise descriptor set. */ FD_ZERO(&wset); FD_SET(http_fd, &wset); timeout.tv_usec = 0L; timeout.tv_sec = transfer_timeout; /* Wait for message x seconds and then continue. */ status = select(http_fd + 1, NULL, &wset, NULL, &timeout); if (status == 0) { /* Timeout has arrived. */ timeout_flag = ON; return(INCORRECT); } else if (status < 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_write", NULL, _("select() error : %s"), strerror(errno)); return(INCORRECT); } else if (FD_ISSET(http_fd, &wset)) { /* * When buffer is not NULL we want to sent the * data in ASCII-mode. */ if (buffer != NULL) { register int i, count = 0; for (i = 0; i < size; i++) { if (*ptr == '\n') { buffer[count++] = '\r'; buffer[count++] = '\n'; } else { buffer[count++] = *ptr; } ptr++; } size = count; ptr = buffer; } #ifdef WITH_SSL if (ssl_con == NULL) { #endif if ((status = write(http_fd, ptr, size)) != size) { if ((errno == ECONNRESET) || (errno == EBADF)) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_write", NULL, _("write() error (%d) : %s"), status, strerror(errno)); return(errno); } #ifdef WITH_SSL } else { if (ssl_write(ssl_con, ptr, size) != size) { return(INCORRECT); } } #endif #ifdef WITH_TRACE trace_log(NULL, 0, BIN_W_TRACE, ptr, size, NULL); #endif } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_write", NULL, _("Unknown condition.")); return(INCORRECT); } return(SUCCESS); } /*############################# http_read() #############################*/ int http_read(char *block, int blocksize) { int bytes_read; if (hmr.bytes_buffered > 0) { if (hmr.bytes_buffered >= blocksize) { #ifdef FUB_DEBUG if (blocksize > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_read", NULL, "Nooooo! Reading beyond buffer blocksize (%d) > MAX_RET_MSG_LENGTH (%d)", blocksize, MAX_RET_MSG_LENGTH); } #endif memcpy(block, msg_str, blocksize); if (hmr.bytes_buffered > blocksize) { hmr.bytes_buffered -= blocksize; #ifdef FUB_DEBUG if (hmr.bytes_buffered > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_read", NULL, "Urghhhh!!! hmr.bytes_buffered (%d) > MAX_RET_MSG_LENGTH (%d)", hmr.bytes_buffered, MAX_RET_MSG_LENGTH); } if ((blocksize + hmr.bytes_buffered) > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_read", NULL, "Nooooo! Reading beyond buffer (blocksize (%d) + hmr.bytes_buffered (%d)) > MAX_RET_MSG_LENGTH (%d)", blocksize, hmr.bytes_buffered, MAX_RET_MSG_LENGTH); } #endif (void)memmove(msg_str, msg_str + blocksize, hmr.bytes_buffered); } else { hmr.bytes_buffered = 0; } hmr.bytes_read = 0; bytes_read = blocksize; } else { #ifdef FUB_DEBUG if (hmr.bytes_buffered > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_read", NULL, "Nooooo! Reading beyond buffer hmr.bytes_buffered (%d) > MAX_RET_MSG_LENGTH (%d)", hmr.bytes_buffered, MAX_RET_MSG_LENGTH); } #endif memcpy(block, msg_str, hmr.bytes_buffered); bytes_read = hmr.bytes_buffered; hmr.bytes_buffered = 0; hmr.bytes_read = 0; } return(bytes_read); } #ifdef WITH_SSL if ((ssl_con != NULL) && (SSL_pending(ssl_con))) { if ((bytes_read = SSL_read(ssl_con, block, blocksize)) == INCORRECT) { int status; if ((status = SSL_get_error(ssl_con, bytes_read)) == SSL_ERROR_SYSCALL) { if (errno == ECONNRESET) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_read", NULL, _("SSL_read() error : %s"), strerror(errno)); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_read", NULL, _("SSL_read() error %d"), status); } return(INCORRECT); } # ifdef WITH_TRACE trace_log(NULL, 0, BIN_R_TRACE, block, bytes_read, NULL); # endif } else #endif { int status; fd_set rset; /* Initialise descriptor set. */ FD_ZERO(&rset); FD_SET(http_fd, &rset); timeout.tv_usec = 0L; timeout.tv_sec = transfer_timeout; /* Wait for message x seconds and then continue. */ status = select(http_fd + 1, &rset, NULL, NULL, &timeout); if (status == 0) { /* Timeout has arrived. */ timeout_flag = ON; return(INCORRECT); } else if (FD_ISSET(http_fd, &rset)) { #ifdef WITH_SSL if (ssl_con == NULL) { #endif if ((bytes_read = read(http_fd, block, blocksize)) == -1) { if (errno == ECONNRESET) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_read", NULL, _("read() error : %s"), strerror(errno)); return(INCORRECT); } #ifdef WITH_SSL } else { int tmp_errno; /* * Remember we have set SSL_MODE_AUTO_RETRY. This * means the SSL lib may do several read() calls. We * just assured one read() with select(). So lets * set an an alarm since we might block on subsequent * calls to read(). It might be better when we reimplement * this without SSL_MODE_AUTO_RETRY and handle * SSL_ERROR_WANT_READ ourself. */ if (sigsetjmp(env_alrm, 1) != 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_read", NULL, _("SSL_read() timeout (%ld)"), transfer_timeout); timeout_flag = ON; return(INCORRECT); } (void)alarm(transfer_timeout); bytes_read = SSL_read(ssl_con, block, blocksize); tmp_errno = errno; (void)alarm(0); if (bytes_read == INCORRECT) { if ((status = SSL_get_error(ssl_con, bytes_read)) == SSL_ERROR_SYSCALL) { if (tmp_errno == ECONNRESET) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_read", NULL, _("SSL_read() error : %s"), strerror(tmp_errno)); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_read", NULL, _("SSL_read() error %d"), status); } return(INCORRECT); } } #endif #ifdef WITH_TRACE trace_log(NULL, 0, BIN_R_TRACE, block, bytes_read, NULL); #endif } else if (status < 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_read", NULL, _("select() error : %s"), strerror(errno)); return(INCORRECT); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_read", NULL, _("Unknown condition.")); return(INCORRECT); } } return(bytes_read); } /*########################## http_chunk_read() ##########################*/ int http_chunk_read(char **chunk, int *chunksize) { int bytes_buffered, read_length; /* First, try read the chunk size. */ if ((bytes_buffered = read_msg(&read_length, 0, __LINE__)) > 0) { int bytes_read, status, tmp_chunksize; fd_set rset; tmp_chunksize = (int)strtol(msg_str, NULL, 16); if (errno == ERANGE) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", msg_str, _("Failed to determine the chunk size with strtol() : %s"), strerror(errno)); return(INCORRECT); } else { if (tmp_chunksize == 0) { hmr.bytes_read = 0; hmr.bytes_buffered = 0; return(HTTP_LAST_CHUNK); } tmp_chunksize += 2; if (tmp_chunksize > *chunksize) { char *tmp_chunk; if ((tmp_chunk = realloc(*chunk, tmp_chunksize)) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("Failed to realloc() %d bytes : %s"), tmp_chunksize, strerror(errno)); free(*chunk); *chunk = NULL; return(INCORRECT); } *chunk = tmp_chunk; *chunksize = tmp_chunksize; } bytes_buffered -= (read_length + 1); #ifdef FUB_DEBUG if ((read_length + 1 + bytes_buffered) > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, "Nooooo! Reading beyond buffer (read_length + 1) (%d) + bytes_buffered (%d) > MAX_RET_MSG_LENGTH (%d)", (read_length + 1), bytes_buffered, MAX_RET_MSG_LENGTH); } #endif if (tmp_chunksize > bytes_buffered) { (void)memcpy(*chunk, msg_str + read_length + 1, bytes_buffered); hmr.bytes_read = 0; } else { (void)memcpy(*chunk, msg_str + read_length + 1, tmp_chunksize - 2); hmr.bytes_read = bytes_buffered - tmp_chunksize; (void)read_msg(NULL, tmp_chunksize, __LINE__); return(tmp_chunksize - 2); } } FD_ZERO(&rset); do { #ifdef WITH_SSL if ((ssl_con != NULL) && (SSL_pending(ssl_con))) { if ((bytes_read = SSL_read(ssl_con, (*chunk + bytes_buffered), tmp_chunksize - bytes_buffered)) == INCORRECT) { if ((status = SSL_get_error(ssl_con, bytes_read)) == SSL_ERROR_SYSCALL) { if (errno == ECONNRESET) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("SSL_read() error : %s"), strerror(errno)); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("SSL_read() error %d"), status); } return(INCORRECT); } if (bytes_read == 0) { /* Premature end, remote side has closed connection. */ trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("Remote side closed connection (expected: %d read: %d)"), tmp_chunksize, bytes_buffered); return(INCORRECT); } # ifdef WITH_TRACE trace_log(NULL, 0, BIN_CMD_R_TRACE, (*chunk + bytes_buffered), bytes_read, NULL); # endif bytes_buffered += bytes_read; } else { #endif FD_SET(http_fd, &rset); timeout.tv_usec = 0L; timeout.tv_sec = transfer_timeout; /* Wait for message x seconds and then continue. */ status = select(http_fd + 1, &rset, NULL, NULL, &timeout); if (status == 0) { /* Timeout has arrived. */ timeout_flag = ON; return(INCORRECT); } else if (FD_ISSET(http_fd, &rset)) { #ifdef WITH_SSL if (ssl_con == NULL) { #endif if ((bytes_read = read(http_fd, (*chunk + bytes_buffered), tmp_chunksize - bytes_buffered)) == -1) { if (errno == ECONNRESET) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("read() error : %s"), strerror(errno)); return(INCORRECT); } #ifdef WITH_SSL } else { int tmp_errno; /* * Remember we have set SSL_MODE_AUTO_RETRY. This * means the SSL lib may do several read() calls. We * just assured one read() with select(). So lets * set an an alarm since we might block on subsequent * calls to read(). It might be better when we * reimplement this without SSL_MODE_AUTO_RETRY * and handle SSL_ERROR_WANT_READ ourself. */ if (sigsetjmp(env_alrm, 1) != 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("SSL_read() timeout (%ld)"), transfer_timeout); timeout_flag = ON; return(INCORRECT); } (void)alarm(transfer_timeout); bytes_read = SSL_read(ssl_con, (*chunk + bytes_buffered), tmp_chunksize - bytes_buffered); tmp_errno = errno; (void)alarm(0); if (bytes_read == INCORRECT) { if ((status = SSL_get_error(ssl_con, bytes_read)) == SSL_ERROR_SYSCALL) { if (tmp_errno == ECONNRESET) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("SSL_read() error : %s"), strerror(tmp_errno)); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("SSL_read() error %d"), status); } return(INCORRECT); } } #endif if (bytes_read == 0) { /* Premature end, remote side has closed connection. */ trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("Remote side closed connection (expected: %d read: %d)"), tmp_chunksize, bytes_buffered); return(INCORRECT); } #ifdef WITH_TRACE trace_log(NULL, 0, BIN_CMD_R_TRACE, (*chunk + bytes_buffered), bytes_read, NULL); #endif bytes_buffered += bytes_read; } else if (status < 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("select() error : %s"), strerror(errno)); return(INCORRECT); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("Unknown condition.")); return(INCORRECT); } #ifdef WITH_SSL } #endif } while (bytes_buffered < tmp_chunksize); if (bytes_buffered == tmp_chunksize) { bytes_buffered -= 2; } } else if (bytes_buffered == 0) { timeout_flag = NEITHER; trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_chunk_read", NULL, _("Remote hang up.")); bytes_buffered = INCORRECT; } return(bytes_buffered); } /*############################ http_noop() ##############################*/ int http_noop(char *path) { int reply; if (http_fd == -1) { reply = PERMANENT_DISCONNECT; } else { int i, j; char resource[8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + 1], tmp_path[MAX_RECIPIENT_LENGTH + 1]; if ((reply = check_connection()) == CONNECTION_REOPENED) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_noop", NULL, _("Reconnected.")); hmr.retries = 1; } else if (reply == INCORRECT) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_noop", NULL, _("Failed to reconnect.")); return(INCORRECT); } if (hmr.http_options_not_working & HTTP_OPTION_HEAD) { hmr.date = 0; hmr.content_length = 0; return(INCORRECT); } hmr.retries = 0; hmr.date = 0; i = 0; if (*path == '/') { j = 1; } else { j = 0; } while ((j < MAX_RECIPIENT_LENGTH) && (i < MAX_RECIPIENT_LENGTH) && (path[j] != '/') && (path[j] != '\0')) { tmp_path[i] = path[j]; i++; j++; } if (path[j] == '/') { tmp_path[i] = '/'; i++; } tmp_path[i] = '\0'; if (hmr.http_proxy[0] == '\0') { (void)snprintf(resource, 1 + MAX_RECIPIENT_LENGTH + 1, "/%s", tmp_path); } else { #ifdef WITH_SSL if (hmr.tls_auth == YES) { (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + 1, "https://%s/%s", hmr.hostname, tmp_path); } else { #endif (void)snprintf(resource, 8 + MAX_REAL_HOSTNAME_LENGTH + 1 + MAX_RECIPIENT_LENGTH + 1, "http://%s/%s", hmr.hostname, tmp_path); #ifdef WITH_SSL } #endif } #ifdef WITH_TRACE trace_log(__FILE__, __LINE__, C_TRACE, NULL, 0, "http_noop(): Trying HEAD on %s", tmp_path); #endif #ifdef WITH_SSL if ((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) { if (aws_cmd("HEAD", "", tmp_path, "", &hmr) != SUCCESS) { return(INCORRECT); } } else { #endif if (hmr.authorization == NULL) { if (hmr.www_authenticate == WWW_AUTHENTICATE_BASIC) { if (basic_authentication(&hmr) != SUCCESS) { return(INCORRECT); } } } else { if ((hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_MD5_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA256_S) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256) || (hmr.www_authenticate == WWW_AUTHENTICATE_DIGEST_SHA512_256_S)) { if (digest_authentication("HEAD", path, "", &hmr) != SUCCESS) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "http_head", NULL, _("Failed to create new digest authentication.")); return(INCORRECT); } } } #ifdef WITH_SSL } #endif /* I do not know of a better way. HTTP does not support */ /* a NOOP command, so lets just do a HEAD on the current */ /* current server. */ retry_noop: if ((reply = test_command(http_fd, "HEAD %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: AFD/%s\r\n%sContent-length: 0\r\nAccept: */*\r\n", resource, hmr.hostname, PACKAGE_VERSION, (hmr.authorization == NULL) ? "" : hmr.authorization)) == SUCCESS) { if ((reply = get_http_reply(NULL, 200, __LINE__)) == 200) { reply = SUCCESS; } else if (reply == 401) /* Unauthorized */ { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; if (init_authentication("HEAD", path, "") == SUCCESS) { if (check_connection() > INCORRECT) { goto retry_noop; } } } else if ((reply == 400) || /* Bad Request */ (reply == 405) || /* Method Not Allowed */ (reply == 403) || /* Forbidden */ (reply == 501)) /* Not Implemented */ { hmr.http_options_not_working |= HTTP_OPTION_HEAD; hmr.bytes_buffered = 0; hmr.bytes_read = 0; } else if (reply == CONNECTION_REOPENED) { goto retry_noop; } else { clear_msg_str(); hmr.bytes_buffered = 0; hmr.bytes_read = 0; } } if (((hmr.auth_type == AUTH_AWS4_HMAC_SHA256) || (hmr.auth_type == AUTH_AWS_NO_SIGN_REQUEST)) && (hmr.authorization != NULL)) { free(hmr.authorization); hmr.authorization = NULL; } } return(reply); } /*############################ http_quit() ##############################*/ void http_quit(void) { if (hmr.free == NO) { hmr.free = YES; } else { if (hmr.authorization != NULL) { free(hmr.authorization); hmr.authorization = NULL; } if (hmr.realm != NULL) { free(hmr.realm); hmr.realm = NULL; } if (hmr.nonce != NULL) { free(hmr.nonce); hmr.nonce = NULL; } if (hmr.opaque != NULL) { free(hmr.opaque); hmr.opaque = NULL; } if (hmr.filename != NULL) { free(hmr.filename); hmr.filename = NULL; } } if (http_fd != -1) { if ((timeout_flag != ON) && (timeout_flag != CON_RESET) && (simulation_mode != YES)) { if (shutdown(http_fd, 1) < 0) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_quit", NULL, _("shutdown() error : %s"), strerror(errno)); } } #ifdef WITH_SSL if (ssl_con != NULL) { if ((timeout_flag != ON) && (timeout_flag != CON_RESET)) { if (SSL_shutdown(ssl_con) == 0) { (void)SSL_shutdown(ssl_con); } } SSL_free(ssl_con); ssl_con = NULL; } #endif if (close(http_fd) == -1) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "http_quit", NULL, _("close() error : %s"), strerror(errno)); } http_fd = -1; } return; } /*++++++++++++++++++++++++ init_authentication() ++++++++++++++++++++++++*/ static int init_authentication(char *method, char *path, char *filename) { int ret; if ((hmr.auth_type == AUTH_NONE) || (hmr.auth_type == AUTH_BASIC) || (hmr.auth_type == AUTH_DIGEST)) { if ((hmr.user[0] == '\0') || (hmr.www_authenticate == WWW_AUTHENTICATE_UNKNOWN)) { ret = INCORRECT; } else { if (hmr.www_authenticate == WWW_AUTHENTICATE_BASIC) { if ((ret = basic_authentication(&hmr)) != SUCCESS) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "init_authentication", NULL, _("Failed to create basic authentication.")); } } else { if ((ret = digest_authentication(method, path, filename, &hmr)) != SUCCESS) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "init_authentication", NULL, _("Failed to create digest authentication.")); } } } } else { ret = INCORRECT; } return(ret); } /*+++++++++++++++++++++++++ check_connection() ++++++++++++++++++++++++++*/ static int check_connection(void) { int connection_closed, status = SUCCESS; if (hmr.close == YES) { connection_closed = YES; hmr.free = NO; http_quit(); } else { int ret; /* * Check if connection is still up, by using recv() with * MSG_PEEK and MSG_DONT_WAIT and if that is 0 the connection * is closed. */ if ((ret = recv(http_fd, NULL, 1, MSG_PEEK | MSG_DONTWAIT)) == 1) { connection_closed = NO; } else { int tmp_timeout_flag; if (hmr.debug > 0) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "check_connection", NULL, "recv() returned %d.", ret); } connection_closed = YES; tmp_timeout_flag = timeout_flag; timeout_flag = ON; /* So http_quit() does not call shutdown. */ hmr.free = NO; /* So http_quit() does not free hmr structure. */ http_quit(); timeout_flag = tmp_timeout_flag; } } if (connection_closed == YES) { if ((status = http_connect(hmr.hostname, hmr.http_proxy, hmr.port, hmr.user, hmr.passwd, hmr.auth_type, hmr.fra_options, hmr.features, #ifdef WITH_SSL hmr.listobject_version, hmr.service_type, hmr.region, hmr.tls_auth, #endif hmr.sndbuf_size, hmr.rcvbuf_size, hmr.debug, NO)) != SUCCESS) { if (hmr.http_proxy[0] == '\0') { trans_log(ERROR_SIGN, __FILE__, __LINE__, "check_connection", msg_str, _("HTTP reconnect to %s at port %d failed (%d)."), hmr.hostname, hmr.port, status); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "check_connection", msg_str, _("HTTP reconnect to HTTP proxy %s at port %d failed (%d)."), hmr.http_proxy, hmr.port, status); } status = INCORRECT; } else { status = CONNECTION_REOPENED; } } return(status); } /*+++++++++++++++++++++++++++ get_http_reply() ++++++++++++++++++++++++++*/ static int get_http_reply(int *ret_bytes_buffered, int reply, int line) { int bytes_buffered, read_length, status_code = INCORRECT; if (simulation_mode == YES) { if (reply == 999) { hmr.content_length = 0; hmr.date = time(NULL); reply = 200; } hmr.chunked = NO; hmr.http_options = HTTP_OPTION_DELETE | HTTP_OPTION_HEAD | HTTP_OPTION_GET | HTTP_OPTION_PUT | HTTP_OPTION_MOVE | HTTP_OPTION_POST | HTTP_OPTION_OPTIONS; return(reply); } /* First read start line. */ hmr.bytes_buffered = 0; if (ret_bytes_buffered != NULL) { *ret_bytes_buffered = 0; } read_msg_again: if ((bytes_buffered = read_msg(&read_length, 0, line)) > 0) { hmr.close = NO; hmr.chunked = NO; if ((read_length > 12) && (((msg_str[0] == 'H') || (msg_str[0] == 'h')) && ((msg_str[1] == 'T') || (msg_str[1] == 't')) && ((msg_str[2] == 'T') || (msg_str[2] == 't')) && ((msg_str[3] == 'P') || (msg_str[3] == 'p')) && (msg_str[4] == '/') && (isdigit((int)(msg_str[5]))) && (msg_str[6] == '.') && (isdigit((int)(msg_str[7]))) && (msg_str[8] == ' ') && (isdigit((int)(msg_str[9]))) && (isdigit((int)(msg_str[10]))) && (isdigit((int)(msg_str[11]))))) { hmr.http_version = ((msg_str[5] - '0') * 10) + (msg_str[7] - '0'); status_code = ((msg_str[9] - '0') * 100) + ((msg_str[10] - '0') * 10) + (msg_str[11] - '0'); if (read_length <= MAX_HTTP_HEADER_BUFFER) { (void)memcpy(hmr.msg_header, msg_str, read_length); hmr.header_length = read_length; } else { (void)memcpy(hmr.msg_header, msg_str, MAX_HTTP_HEADER_BUFFER - 1); hmr.msg_header[MAX_HTTP_HEADER_BUFFER - 1] = '\0'; hmr.header_length = MAX_HTTP_HEADER_BUFFER; } /* * Now lets read the headers and lets store that what we * think might be useful later. */ for (;;) { /* * Read line by line and evaluate it. */ if ((bytes_buffered = read_msg(&read_length, 0, line)) <= 0) { if (bytes_buffered == 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, _("Remote hang up. [%d]"), line); timeout_flag = NEITHER; } return(INCORRECT); } /* Check if we have reached header end. */ if ((read_length == 1) && (msg_str[0] == '\0')) { break; } /* Content-Length: */ if ((read_length > 15) && (msg_str[14] == ':') && ((msg_str[8] == 'L') || (msg_str[8] == 'l')) && ((msg_str[9] == 'e') || (msg_str[9] == 'E')) && ((msg_str[10] == 'n') || (msg_str[10] == 'N')) && ((msg_str[11] == 'g') || (msg_str[11] == 'G')) && ((msg_str[12] == 't') || (msg_str[12] == 'T')) && ((msg_str[13] == 'h') || (msg_str[13] == 'H')) && ((msg_str[0] == 'C') || (msg_str[0] == 'c')) && ((msg_str[1] == 'o') || (msg_str[1] == 'O')) && ((msg_str[2] == 'n') || (msg_str[2] == 'N')) && ((msg_str[3] == 't') || (msg_str[3] == 'T')) && ((msg_str[4] == 'e') || (msg_str[4] == 'E')) && ((msg_str[5] == 'n') || (msg_str[5] == 'N')) && ((msg_str[6] == 't') || (msg_str[6] == 'T')) && (msg_str[7] == '-')) { int i = 15; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } if (i < read_length) { int k = i; while ((k < read_length) && (isdigit((int)(msg_str[k])))) { k++; } if (k > 0) { if (msg_str[k] != '\0') { msg_str[k] = '\0'; } errno = 0; hmr.content_length = (off_t)str2offt(&msg_str[i], NULL, 10); if (errno != 0) { hmr.content_length = 0; } } } } /* Connection: */ else if ((read_length > 11) && (msg_str[10] == ':') && ((msg_str[0] == 'C') || (msg_str[0] == 'c')) && ((msg_str[1] == 'o') || (msg_str[1] == 'O')) && ((msg_str[2] == 'n') || (msg_str[2] == 'N')) && ((msg_str[3] == 'n') || (msg_str[3] == 'N')) && ((msg_str[4] == 'e') || (msg_str[4] == 'E')) && ((msg_str[5] == 'c') || (msg_str[5] == 'C')) && ((msg_str[6] == 't') || (msg_str[6] == 'T')) && ((msg_str[7] == 'i') || (msg_str[7] == 'I')) && ((msg_str[8] == 'o') || (msg_str[8] == 'O')) && ((msg_str[9] == 'n') || (msg_str[9] == 'N'))) { int i = 11; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } if (((i + 4) <= read_length) && ((msg_str[i] == 'c') || (msg_str[i] == 'C')) && ((msg_str[i + 1] == 'l') || (msg_str[i + 1] == 'L')) && ((msg_str[i + 2] == 'o') || (msg_str[i + 2] == 'O')) && ((msg_str[i + 3] == 's') || (msg_str[i + 3] == 'S')) && ((msg_str[i + 4] == 'e') || (msg_str[i + 4] == 'E'))) { hmr.close = YES; } } /* WWW-Authenticate: */ else if ((read_length > 17) && (msg_str[16] == ':') && (msg_str[3] == '-') && ((msg_str[0] == 'W') || (msg_str[0] == 'w')) && ((msg_str[1] == 'W') || (msg_str[1] == 'w')) && ((msg_str[2] == 'W') || (msg_str[2] == 'w')) && ((msg_str[4] == 'A') || (msg_str[4] == 'a')) && ((msg_str[5] == 'u') || (msg_str[5] == 'U')) && ((msg_str[6] == 't') || (msg_str[6] == 'T')) && ((msg_str[7] == 'h') || (msg_str[7] == 'H')) && ((msg_str[8] == 'e') || (msg_str[8] == 'E')) && ((msg_str[9] == 'n') || (msg_str[9] == 'N')) && ((msg_str[10] == 't') || (msg_str[10] == 'T')) && ((msg_str[11] == 'i') || (msg_str[11] == 'I')) && ((msg_str[12] == 'c') || (msg_str[12] == 'C')) && ((msg_str[13] == 'a') || (msg_str[13] == 'A')) && ((msg_str[14] == 't') || (msg_str[14] == 'T')) && ((msg_str[15] == 'e') || (msg_str[15] == 'E'))) { int i = 17; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } if ((i + 4) < read_length) { /* Basic */ if (((msg_str[i] == 'B') || (msg_str[i] == 'b')) && ((msg_str[i + 1] == 'a') || (msg_str[i + 1] == 'A')) && ((msg_str[i + 2] == 's') || (msg_str[i + 2] == 'S')) && ((msg_str[i + 3] == 'i') || (msg_str[i + 3] == 'I')) && ((msg_str[i + 4] == 'c') || (msg_str[i + 4] == 'C'))) { hmr.www_authenticate = WWW_AUTHENTICATE_BASIC; } /* Digest */ else if (((i + 6) < read_length) && ((msg_str[i] == 'D') || (msg_str[i] == 'd')) && ((msg_str[i + 1] == 'i') || (msg_str[i + 1] == 'I')) && ((msg_str[i + 2] == 'g') || (msg_str[i + 2] == 'G')) && ((msg_str[i + 3] == 'e') || (msg_str[i + 3] == 'E')) && ((msg_str[i + 4] == 's') || (msg_str[i + 4] == 'S')) && ((msg_str[i + 5] == 't') || (msg_str[i + 5] == 'T'))) { i += 6; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } if (store_http_digest(i, read_length) == INCORRECT) { status_code = INCORRECT; } } } } /* Transfer-Encoding: */ else if ((read_length > 18) && (msg_str[17] == ':') && (msg_str[8] == '-') && ((msg_str[0] == 'T') || (msg_str[0] == 't')) && ((msg_str[1] == 'r') || (msg_str[1] == 'R')) && ((msg_str[2] == 'a') || (msg_str[2] == 'A')) && ((msg_str[3] == 'n') || (msg_str[3] == 'N')) && ((msg_str[4] == 's') || (msg_str[4] == 'S')) && ((msg_str[5] == 'f') || (msg_str[5] == 'F')) && ((msg_str[6] == 'e') || (msg_str[6] == 'E')) && ((msg_str[7] == 'r') || (msg_str[7] == 'R')) && ((msg_str[9] == 'E') || (msg_str[9] == 'e')) && ((msg_str[10] == 'n') || (msg_str[10] == 'N')) && ((msg_str[11] == 'c') || (msg_str[11] == 'C')) && ((msg_str[12] == 'o') || (msg_str[12] == 'O')) && ((msg_str[13] == 'd') || (msg_str[13] == 'D')) && ((msg_str[14] == 'i') || (msg_str[14] == 'I')) && ((msg_str[15] == 'n') || (msg_str[15] == 'N')) && ((msg_str[16] == 'g') || (msg_str[16] == 'G'))) { int i = 18; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } if ((i + 7) < read_length) { /* chunked */ if (((msg_str[i] == 'c') || (msg_str[i] == 'C')) && ((msg_str[i + 1] == 'h') || (msg_str[i + 1] == 'H')) && ((msg_str[i + 2] == 'u') || (msg_str[i + 2] == 'U')) && ((msg_str[i + 3] == 'n') || (msg_str[i + 3] == 'N')) && ((msg_str[i + 4] == 'k') || (msg_str[i + 4] == 'K')) && ((msg_str[i + 5] == 'e') || (msg_str[i + 5] == 'E')) && ((msg_str[i + 6] == 'd') || (msg_str[i + 6] == 'D'))) { hmr.chunked = YES; } } } /* Last-Modified: */ else if ((hmr.date != -1) && (read_length > 14) && (msg_str[13] == ':') && (msg_str[4] == '-') && ((msg_str[0] == 'L') || (msg_str[0] == 'l')) && ((msg_str[1] == 'a') || (msg_str[1] == 'A')) && ((msg_str[2] == 's') || (msg_str[2] == 'S')) && ((msg_str[3] == 't') || (msg_str[3] == 'T')) && ((msg_str[5] == 'M') || (msg_str[5] == 'm')) && ((msg_str[6] == 'o') || (msg_str[6] == 'O')) && ((msg_str[7] == 'd') || (msg_str[7] == 'D')) && ((msg_str[8] == 'i') || (msg_str[8] == 'I')) && ((msg_str[9] == 'f') || (msg_str[9] == 'F')) && ((msg_str[10] == 'i') || (msg_str[10] == 'I')) && ((msg_str[11] == 'e') || (msg_str[11] == 'E')) && ((msg_str[12] == 'd') || (msg_str[12] == 'D'))) { int i = 14; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } if (i < read_length) { hmr.date = datestr2unixtime(&msg_str[i], NULL); } } #ifdef _WITH_EXTRA_CHECK /* ETag: */ else if ((read_length > 5) && (msg_str[4] == ':') && ((msg_str[0] == 'E') || (msg_str[0] == 'e')) && ((msg_str[1] == 'T') || (msg_str[1] == 't')) && ((msg_str[2] == 'a') || (msg_str[2] == 'A')) && ((msg_str[3] == 'g') || (msg_str[3] == 'G'))) { int i = 5; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } store_http_etag(i, read_length); } #endif /* Allow: */ else if ((read_length > 6) && (msg_str[5] == ':') && ((msg_str[0] == 'A') || (msg_str[0] == 'a')) && ((msg_str[1] == 'l') || (msg_str[1] == 'L')) && ((msg_str[2] == 'l') || (msg_str[2] == 'L')) && ((msg_str[3] == 'o') || (msg_str[3] == 'O')) && ((msg_str[4] == 'w') || (msg_str[4] == 'W'))) { int i = 6; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } store_http_options(i, read_length); } /* Content-Disposition: */ else if ((read_length > 20) && (msg_str[19] == ':') && ((msg_str[8] == 'D') || (msg_str[8] == 'd')) && ((msg_str[9] == 'i') || (msg_str[9] == 'I')) && ((msg_str[10] == 's') || (msg_str[10] == 'S')) && ((msg_str[11] == 'p') || (msg_str[11] == 'P')) && ((msg_str[12] == 'o') || (msg_str[12] == 'O')) && ((msg_str[13] == 's') || (msg_str[13] == 'S')) && ((msg_str[14] == 'i') || (msg_str[14] == 'I')) && ((msg_str[15] == 't') || (msg_str[15] == 'T')) && ((msg_str[16] == 'i') || (msg_str[16] == 'I')) && ((msg_str[17] == 'o') || (msg_str[17] == 'O')) && ((msg_str[18] == 'n') || (msg_str[18] == 'N')) && ((msg_str[0] == 'C') || (msg_str[0] == 'c')) && ((msg_str[1] == 'o') || (msg_str[1] == 'O')) && ((msg_str[2] == 'n') || (msg_str[2] == 'N')) && ((msg_str[3] == 't') || (msg_str[3] == 'T')) && ((msg_str[4] == 'e') || (msg_str[4] == 'E')) && ((msg_str[5] == 'n') || (msg_str[5] == 'N')) && ((msg_str[6] == 't') || (msg_str[6] == 'T')) && (msg_str[7] == '-')) { int i = 20; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } /* attachment; */ if (((i + 11) <= read_length) && ((msg_str[i] == 'a') || (msg_str[i] == 'A')) && ((msg_str[i + 1] == 't') || (msg_str[i + 1] == 'T')) && ((msg_str[i + 2] == 't') || (msg_str[i + 2] == 'T')) && ((msg_str[i + 3] == 'a') || (msg_str[i + 3] == 'A')) && ((msg_str[i + 4] == 'c') || (msg_str[i + 4] == 'C')) && ((msg_str[i + 5] == 'h') || (msg_str[i + 5] == 'H')) && ((msg_str[i + 6] == 'm') || (msg_str[i + 6] == 'M')) && ((msg_str[i + 7] == 'e') || (msg_str[i + 7] == 'E')) && ((msg_str[i + 8] == 'n') || (msg_str[i + 8] == 'N')) && ((msg_str[i + 9] == 't') || (msg_str[i + 9] == 'T')) && (msg_str[i + 10] == ';')) { i = i + 11; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } if (i < read_length) { /* filename= */ if (((i + 9) <= read_length) && ((msg_str[i] == 'f') || (msg_str[i] == 'F')) && ((msg_str[i + 1] == 'i') || (msg_str[i + 1] == 'I')) && ((msg_str[i + 2] == 'l') || (msg_str[i + 2] == 'L')) && ((msg_str[i + 3] == 'e') || (msg_str[i + 3] == 'E')) && ((msg_str[i + 4] == 'n') || (msg_str[i + 4] == 'N')) && ((msg_str[i + 5] == 'a') || (msg_str[i + 5] == 'A')) && ((msg_str[i + 6] == 'm') || (msg_str[i + 6] == 'M')) && ((msg_str[i + 7] == 'e') || (msg_str[i + 7] == 'E')) && (msg_str[i + 8] == '=')) { char end_char; /* Ignore any leading space. */ while (((i + 9) < read_length) && (msg_str[i + 9] == ' ')) { i++; } if (msg_str[i + 9] == '"') { i = i + 10; end_char = '"'; } else { i = i + 9; end_char = '\0'; } if ((msg_str[i] == '.') || (msg_str[i] == '/')) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, _("Filename may not start with `%c'."), msg_str[i]); } else { if ((hmr.filename == NULL) && ((hmr.filename = malloc(MAX_FILENAME_LENGTH + 1)) == NULL)) { trans_log(WARN_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, _("malloc() error : %s"), strerror(errno)); } else { int j = 0; while ((i < read_length) && (j < MAX_FILENAME_LENGTH) && (isascii((int)msg_str[i]) != 0) && (msg_str[i] != end_char) && (msg_str[i] != '/')) { hmr.filename[j] = msg_str[i]; i++; j++; } if (msg_str[i] == end_char) { hmr.filename[j] = '\0'; } else { if (j == MAX_FILENAME_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Filename larger then %d bytes", MAX_FILENAME_LENGTH); } else if (i == read_length) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Premature end of buffer reached."); } else if (msg_str[i] == '/') { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Filename may not contain directory information."); } else { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Filename contains non ASCII character (%d).", (int)msg_str[i]); } hmr.filename[0] = '\0'; } } } } } } else { /* filename= */ if (((i + 9) <= read_length) && ((msg_str[i] == 'f') || (msg_str[i] == 'F')) && ((msg_str[i + 1] == 'i') || (msg_str[i + 1] == 'I')) && ((msg_str[i + 2] == 'l') || (msg_str[i + 2] == 'L')) && ((msg_str[i + 3] == 'e') || (msg_str[i + 3] == 'E')) && ((msg_str[i + 4] == 'n') || (msg_str[i + 4] == 'N')) && ((msg_str[i + 5] == 'a') || (msg_str[i + 5] == 'A')) && ((msg_str[i + 6] == 'm') || (msg_str[i + 6] == 'M')) && ((msg_str[i + 7] == 'e') || (msg_str[i + 7] == 'E')) && (msg_str[i + 8] == '=')) { char end_char; /* Ignore any leading space. */ while (((i + 9) < read_length) && (msg_str[i + 9] == ' ')) { i++; } if (msg_str[i + 9] == '"') { i = i + 10; end_char = '"'; } else { i = i + 9; end_char = '\0'; } if ((msg_str[i] == '.') || (msg_str[i] == '/')) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, _("Filename may not start with `%c'."), msg_str[i]); } else { if ((hmr.filename == NULL) && ((hmr.filename = malloc(MAX_FILENAME_LENGTH + 1)) == NULL)) { trans_log(WARN_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, _("malloc() error : %s"), strerror(errno)); } else { int j = 0; while ((i < read_length) && (j < MAX_FILENAME_LENGTH) && (isascii((int)msg_str[i]) != 0) && (msg_str[i] != end_char) && (msg_str[i] != '/')) { hmr.filename[j] = msg_str[i]; i++; j++; } if (msg_str[i] == end_char) { hmr.filename[j] = '\0'; } else { if (j == MAX_FILENAME_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Filename larger then %d bytes", MAX_FILENAME_LENGTH); } else if (i == read_length) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Premature end of buffer reached."); } else if (msg_str[i] == '/') { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Filename may not contain directory information."); } else { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Filename contains non ASCII character (%d).", (int)msg_str[i]); } hmr.filename[0] = '\0'; } } } } } } /* Authentication-Info: */ else if ((read_length > 19) && (msg_str[19] == ':') && ((msg_str[0] == 'A') || (msg_str[0] == 'a')) && ((msg_str[1] == 'u') || (msg_str[1] == 'U')) && ((msg_str[2] == 't') || (msg_str[2] == 'T')) && ((msg_str[3] == 'h') || (msg_str[3] == 'H')) && ((msg_str[4] == 'e') || (msg_str[4] == 'E')) && ((msg_str[5] == 'n') || (msg_str[5] == 'N')) && ((msg_str[6] == 't') || (msg_str[6] == 'T')) && ((msg_str[7] == 'i') || (msg_str[7] == 'I')) && ((msg_str[8] == 'c') || (msg_str[8] == 'C')) && ((msg_str[9] == 'a') || (msg_str[9] == 'A')) && ((msg_str[10] == 't') || (msg_str[10] == 'T')) && ((msg_str[11] == 'i') || (msg_str[11] == 'I')) && ((msg_str[12] == 'o') || (msg_str[12] == 'O')) && ((msg_str[13] == 'n') || (msg_str[13] == 'N')) && (msg_str[14] == '-') && ((msg_str[15] == 'I') || (msg_str[15] == 'i')) && ((msg_str[16] == 'n') || (msg_str[16] == 'N')) && ((msg_str[17] == 'f') || (msg_str[17] == 'F')) && ((msg_str[18] == 'o') || (msg_str[18] == 'O'))) { int i = 20; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } /* * For digest authentication, check if the server * provides a nextnonce. */ store_http_digest(i, read_length); } } /* for (;;) */ if ((ret_bytes_buffered != NULL) && (bytes_buffered > read_length)) { *ret_bytes_buffered = bytes_buffered - read_length - 1; if (msg_str[0] != '\0') { #ifdef FUB_DEBUG if (*ret_bytes_buffered > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Urghhhh!!! *ret_bytes_buffered (%d) > MAX_RET_MSG_LENGTH (%d)", *ret_bytes_buffered, MAX_RET_MSG_LENGTH); } if ((read_length + 1 + *ret_bytes_buffered) > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Nooooo! Reading beyond buffer (read_length (%d) + 1 + *ret_bytes_buffered (%d)) > MAX_RET_MSG_LENGTH (%d)", read_length, *ret_bytes_buffered, MAX_RET_MSG_LENGTH); } #endif (void)memmove(msg_str, msg_str + read_length + 1, *ret_bytes_buffered); } } if ((read_length == 1) && (msg_str[0] == '\0') && (msg_str[1] == '\n')) { #ifdef FUB_DEBUG if ((bytes_buffered - 2) > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Urghhhh!!! (bytes_buffered (%d) - 2) > MAX_RET_MSG_LENGTH (%d)", bytes_buffered, MAX_RET_MSG_LENGTH); } if (bytes_buffered > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, "Nooooo! Reading beyond buffer bytes_buffered (%d) > MAX_RET_MSG_LENGTH (%d)", bytes_buffered, MAX_RET_MSG_LENGTH); } #endif (void)memmove(msg_str, msg_str + 2, (bytes_buffered - 2)); (void)read_msg(NULL, -2, line); } } /* * We are expecting a HTTP reply. If read_msg() just manages * to read some bytes, assume something went wrong and * try again. */ else if (read_length < 3) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, _("Ignoring %d bytes and retry read_msg() (bytes_buffered = %d [%d]) [%d]"), read_length, bytes_buffered, (int)msg_str[0], line); goto read_msg_again; } } else if (bytes_buffered == 0) { if (hmr.retries == 0) { if ((status_code = check_connection()) == CONNECTION_REOPENED) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, _("Reconnected. [%d]"), line); hmr.retries = 1; } } else { timeout_flag = NEITHER; trans_log(ERROR_SIGN, __FILE__, __LINE__, "get_http_reply", NULL, _("Remote hang up. [%d]"), line); status_code = INCORRECT; } } #ifdef DEBUG if (status_code == INCORRECT) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "get_http_reply", msg_str, _("Returning INCORRECT (bytes_buffered = %d) [%d]"), bytes_buffered, line); } #endif return(status_code); } /*----------------------------- read_msg() ------------------------------*/ /* ------------ */ /* Reads blockwise from http_fd until it has read one complete line. */ /* From this line it removes the \r\n and inserts a \0 and returns the */ /* the number of bytes it buffered. The number of bytes in the line are */ /* returned by read_length. */ /*-----------------------------------------------------------------------*/ static int read_msg(int *read_length, int offset, int line) { static int bytes_buffered; static char *read_ptr = NULL; int status; fd_set rset; if (read_length == NULL) { if (read_ptr != NULL) { read_ptr += offset; } return(0); } *read_length = 0; if (hmr.bytes_read == 0) { bytes_buffered = 0; } else { #ifdef FUB_DEBUG if (hmr.bytes_read > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "read_msg", NULL, "Urghhhh!!! hmr.bytes_read (%d) > MAX_RET_MSG_LENGTH (%d)", hmr.bytes_read, MAX_RET_MSG_LENGTH); } #endif (void)memmove(msg_str, read_ptr, hmr.bytes_read); bytes_buffered = hmr.bytes_read; read_ptr = msg_str; } FD_ZERO(&rset); for (;;) { if (hmr.bytes_read <= 0) { #ifdef WITH_SSL if ((ssl_con != NULL) && (SSL_pending(ssl_con))) { if ((hmr.bytes_read = SSL_read(ssl_con, &msg_str[bytes_buffered], (MAX_RET_MSG_LENGTH - bytes_buffered))) < 1) { if (hmr.bytes_read == 0) { return(0); } else { if ((status = SSL_get_error(ssl_con, hmr.bytes_read)) == SSL_ERROR_SYSCALL) { if (errno == ECONNRESET) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "read_msg", NULL, _("SSL_read() error (after reading %d bytes) [%d] : %s"), bytes_buffered, line, strerror(errno)); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "read_msg", NULL, _("SSL_read() error (after reading %d bytes) (%d) [%d]"), bytes_buffered, status, line); } hmr.bytes_read = 0; return(INCORRECT); } } # ifdef WITH_TRACE trace_log(NULL, 0, BIN_CMD_R_TRACE, &msg_str[bytes_buffered], hmr.bytes_read, NULL); # endif read_ptr = &msg_str[bytes_buffered]; bytes_buffered += hmr.bytes_read; } else { #endif /* Initialise descriptor set. */ FD_SET(http_fd, &rset); timeout.tv_usec = 0L; timeout.tv_sec = transfer_timeout; /* Wait for message x seconds and then continue. */ status = select(http_fd + 1, &rset, NULL, NULL, &timeout); if (status == 0) { /* Timeout has arrived. */ timeout_flag = ON; hmr.bytes_read = 0; return(INCORRECT); } else if (FD_ISSET(http_fd, &rset)) { #ifdef WITH_SSL if (ssl_con == NULL) { #endif if ((hmr.bytes_read = read(http_fd, &msg_str[bytes_buffered], (MAX_RET_MSG_LENGTH - bytes_buffered))) < 1) { if (hmr.bytes_read == 0) { #ifdef WITH_TRACE trace_log(__FILE__, __LINE__, R_TRACE, NULL, 0, "read_msg(): 0 bytes read"); #endif return(0); } else { if (errno == ECONNRESET) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "read_msg", NULL, _("read() error (after reading %d bytes) [%d] : %s"), bytes_buffered, line, strerror(errno)); hmr.bytes_read = 0; return(INCORRECT); } } #ifdef WITH_SSL } else { int tmp_errno; /* * Remember we have set SSL_MODE_AUTO_RETRY. This * means the SSL lib may do several read() calls. We * just assured one read() with select(). So lets * set an an alarm since we might block on subsequent * calls to read(). It might be better when we * reimplement this without SSL_MODE_AUTO_RETRY * and handle SSL_ERROR_WANT_READ ourself. */ if (sigsetjmp(env_alrm, 1) != 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "read_msg", NULL, _("SSL_read() timeout (%ld)"), transfer_timeout); timeout_flag = ON; return(INCORRECT); } (void)alarm(transfer_timeout); hmr.bytes_read = SSL_read(ssl_con, &msg_str[bytes_buffered], (MAX_RET_MSG_LENGTH - bytes_buffered)); tmp_errno = errno; (void)alarm(0); if (hmr.bytes_read < 1) { if (hmr.bytes_read == 0) { # ifdef WITH_TRACE trace_log(__FILE__, __LINE__, R_TRACE, NULL, 0, "read_msg(): 0 bytes read"); # endif return(0); } else { if ((status = SSL_get_error(ssl_con, hmr.bytes_read)) == SSL_ERROR_SYSCALL) { if (tmp_errno == ECONNRESET) { timeout_flag = CON_RESET; } trans_log(ERROR_SIGN, __FILE__, __LINE__, "read_msg", NULL, _("SSL_read() error (after reading %d bytes) [%d] : %s"), bytes_buffered, line, strerror(tmp_errno)); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "read_msg", NULL, _("SSL_read() error (after reading %d bytes) (%d) [%d]"), bytes_buffered, status, line); } hmr.bytes_read = 0; return(INCORRECT); } } } #endif #ifdef WITH_TRACE trace_log(NULL, 0, BIN_CMD_R_TRACE, &msg_str[bytes_buffered], hmr.bytes_read, NULL); trace_log(__FILE__, __LINE__, R_TRACE, NULL, 0, "read_msg(): %d bytes read", hmr.bytes_read); #endif read_ptr = &msg_str[bytes_buffered]; bytes_buffered += hmr.bytes_read; } else if (status < 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "read_msg", NULL, _("select() error [%d] : %s"), line, strerror(errno)); return(INCORRECT); } else { trans_log(ERROR_SIGN, __FILE__, __LINE__, "read_msg", NULL, _("Unknown condition. [%d]"), line); return(INCORRECT); } #ifdef WITH_SSL } #endif } /* Evaluate what we have read. */ do { if (*read_ptr == '\n') { if (*(read_ptr - 1) == '\r') { *(read_ptr - 1) = '\0'; hmr.bytes_read--; } else { *read_ptr = '\0'; } *read_length = read_ptr - msg_str; read_ptr++; #ifdef WITH_TRACE trace_log(NULL, 0, R_TRACE, msg_str, *read_length, NULL); #endif return(bytes_buffered); } read_ptr++; hmr.bytes_read--; } while (hmr.bytes_read > 0); } /* for (;;) */ } /*--------------------------- clear_msg_str() ---------------------------*/ static void clear_msg_str(void) { int try_restore_msg_buffer; char extra_msg[MAX_EXTRA_RETURN_STR_LENGTH + 1]; if (hmr.bytes_buffered > 0) { try_restore_msg_buffer = YES; } else { try_restore_msg_buffer = NO; } if ((http_fd != -1) && (flush_read(extra_msg) == NO) && (hmr.chunked == YES)) { read_last_chunk(); } if (try_restore_msg_buffer == YES) { /* get_http_reply() has already overwritten the */ /* error message since read_msg() has read more */ /* then required. So in msg_str there is now */ /* garbage. Let's try to restore at least the */ /* first line. */ if (hmr.header_length > 0) { if (extra_msg[0] == '\0') { #ifdef FUB_DEBUG if (hmr.header_length > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "read_msg", NULL, "Urghhhh!!! hmr.header_length (%d) > MAX_RET_MSG_LENGTH (%d)", hmr.header_length, MAX_RET_MSG_LENGTH); } #endif (void)memcpy(msg_str, hmr.msg_header, hmr.header_length); } else { (void)snprintf(msg_str, MAX_RET_MSG_LENGTH - 1, "%s (%s)", hmr.msg_header, extra_msg); } } else { /* So we do not show any garbage. */ (void)snprintf(msg_str, MAX_RET_MSG_LENGTH - 1, "%s", extra_msg); } } else { if ((msg_str[0] == '\0') && (hmr.header_length > 0)) { #ifdef FUB_DEBUG if (hmr.header_length > MAX_RET_MSG_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "read_msg", NULL, "Urghhhh!!! hmr.header_length (%d) > MAX_RET_MSG_LENGTH (%d)", hmr.header_length, MAX_RET_MSG_LENGTH); } #endif (void)memcpy(msg_str, hmr.msg_header, hmr.header_length); } } return; } /*----------------------------- flush_read() ----------------------------*/ /* ------------ */ /* Some HTTP servers return warn/error information in human readable */ /* form, that we do not need. We must however read the complete message, */ /* otherwise the command/reponce sequence will get mixed up. */ /*-----------------------------------------------------------------------*/ static int flush_read(char *extra_return_str) { off_t content_length; if (hmr.content_length == -1) { content_length = hmr.bytes_buffered; } else { content_length = hmr.content_length; } if (content_length != 0) { int bytes_buffered, hunk_size; off_t total_read = 0; char buffer[4096]; #ifdef WITH_TRACE trace_log(__FILE__, __LINE__, R_TRACE, NULL, 0, # if SIZEOF_OFF_T == 4 "Flush reading %ld bytes (hmr.content_length = %ld hmr.bytes_read = %d bufferd bytes = %d).", # else "Flush reading %lld bytes (hmr.content_length = %lld hmr.bytes_read = %d bufferd bytes = %d).", # endif (pri_off_t)content_length, (pri_off_t)hmr.content_length, hmr.bytes_read, hmr.bytes_buffered); #endif while (total_read != content_length) { hunk_size = content_length - total_read; if (hunk_size > 4096) { hunk_size = 4096; } #ifdef WITH_TRACE trace_log(__FILE__, __LINE__, R_TRACE, NULL, 0, "Reading hunk size = %d bytes.", hunk_size); #endif if ((bytes_buffered = http_read(buffer, hunk_size)) <= 0) { #ifdef WITH_TRACE trace_log(__FILE__, __LINE__, R_TRACE, NULL, 0, # if SIZEOF_OFF_T == 4 "No good read %d, flushed %ld bytes.", # else "No good read %d, flushed %lld bytes.", # endif bytes_buffered, (pri_off_t)total_read); #endif return(NO); } total_read += bytes_buffered; } #ifdef WITH_TRACE trace_log(__FILE__, __LINE__, R_TRACE, NULL, 0, # if SIZEOF_OFF_T == 4 "Flushed %ld bytes.", (pri_off_t)total_read); # else "Flushed %lld bytes.", (pri_off_t)total_read); # endif #endif if (extra_return_str != NULL) { extra_return_str[0] = '\0'; /* Lets see if we can get some useful information that we */ /* can return in the reply message. Here we just try to */ /* see if it is XML and it has the and tag.*/ /* If that is the case, try put it into msg_str. */ if (total_read == hmr.content_length) { /* 16) && (buffer[0] == '<') && (buffer[1] == '?') && (buffer[2] == 'x') && (buffer[3] == 'm') && (buffer[4] == 'l') && (buffer[5] == ' ') && (buffer[6] == 'v') && (buffer[7] == 'e') && (buffer[8] == 'r') && (buffer[9] == 's') && (buffer[10] == 'i') && (buffer[11] == 'o') && (buffer[12] == 'n') && (buffer[13] == '=') && (buffer[14] == '"')) { int rpos = 15; while ((rpos < total_read) && (buffer[rpos] != '\n')) { rpos++; } if (buffer[rpos] == '\n') { rpos++; /* */ if (((rpos + 14) < total_read) && (buffer[rpos] == '<') && ((buffer[rpos + 1] == 'E') || (buffer[rpos + 1] == 'e')) && ((buffer[rpos + 2] == 'r') || (buffer[rpos + 2] == 'R')) && ((buffer[rpos + 3] == 'r') || (buffer[rpos + 3] == 'R')) && ((buffer[rpos + 4] == 'o') || (buffer[rpos + 4] == 'O')) && ((buffer[rpos + 5] == 'r') || (buffer[rpos + 5] == 'R')) && (buffer[rpos + 6] == '>') && (buffer[rpos + 7] == '<') && ((buffer[rpos + 8] == 'C') || (buffer[rpos + 8] == 'c')) && ((buffer[rpos + 9] == 'o') || (buffer[rpos + 9] == 'O')) && ((buffer[rpos + 10] == 'd') || (buffer[rpos + 10] == 'D')) && ((buffer[rpos + 11] == 'e') || (buffer[rpos + 11] == 'E')) && (buffer[rpos + 12] == '>')) { int wpos = 0; rpos += 13; while ((wpos < MAX_EXTRA_RETURN_STR_LENGTH) && (rpos < total_read) && (isascii((int)buffer[rpos])) && (buffer[rpos] != '<')) { extra_return_str[wpos] = buffer[rpos]; wpos++; rpos++; } extra_return_str[wpos] = '\0'; } } } } } if ((bytes_buffered > 4) && (buffer[bytes_buffered - 1] == 10) && (buffer[bytes_buffered - 2] == 13) && (buffer[bytes_buffered - 3] == 10) && (buffer[bytes_buffered - 4] == 13) && (buffer[bytes_buffered - 5] == 48)) { return(YES); } } return(NO); } /*-------------------------- read_last_chunk() --------------------------*/ static void read_last_chunk(void) { int bytes_read; char buffer[5]; if ((bytes_read = http_read(buffer, 5)) <= 0) { #ifdef WITH_TRACE trace_log(__FILE__, __LINE__, R_TRACE, NULL, 0, "read_last_chunk(): No good read %d (%d).", bytes_read, hmr.bytes_buffered); #endif } else { #ifdef WITH_TRACE trace_log(__FILE__, __LINE__, R_TRACE, NULL, 0, "read_last_chunk(): Flushed %d bytes (%d).", bytes_read, hmr.bytes_buffered); #endif } return; } #ifdef _WITH_EXTRA_CHECK /*-------------------------- store_http_etag() --------------------------*/ static void store_http_etag(int i, int read_length) { if ((read_length > 2) && (msg_str[i] == 'W') && (msg_str[i + 1] == '/')) { hmr.http_weak_etag = YES; i += 2; } else { hmr.http_weak_etag = NO; } if (msg_str[i] == '"') { int j = 0; i++; while ((i < read_length) && (j < MAX_EXTRA_LS_DATA_LENGTH) && (msg_str[i] != '\0') && (msg_str[i] != '"')) { hmr.http_etag[j] = msg_str[i]; i++; j++; } if (msg_str[i] != '"') { if (j >= MAX_EXTRA_LS_DATA_LENGTH) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "store_http_etag", NULL, _("Buffer for storing ETAG not long enough, may only be %d bytes long."), MAX_EXTRA_LS_DATA_LENGTH); } else if (msg_str[i] == '\0') { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "store_http_etag", msg_str, _("ETAG not terminated properly.")); } else { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "store_http_etag", msg_str, _("Unable to store ETAG. Premature end of buffer [i=%d read_length=%d]."), i, read_length); } j = 0; /* Clear storage for ETAG. */ } hmr.http_etag[j] = '\0'; } else { hmr.http_etag[0] = '\0'; } return; } #endif /*------------------------- store_http_digest() -------------------------*/ static int store_http_digest(int i, int read_length) { int length; while ((i < read_length) && (msg_str[i] != '\0')) { /* realm= */ if ((read_length > (i + 6)) && ((msg_str[i] == 'r') || (msg_str[i] == 'R')) && ((msg_str[i + 1] == 'e') || (msg_str[i + 1] == 'E')) && ((msg_str[i + 2] == 'a') || (msg_str[i + 2] == 'A')) && ((msg_str[i + 3] == 'l') || (msg_str[i + 3] == 'L')) && ((msg_str[i + 4] == 'm') || (msg_str[i + 4] == 'M')) && (msg_str[i + 5] == '=') && (msg_str[i + 6] == '"')) { i += 7; length = 0; while ((i < read_length) && (msg_str[i + length] != '"') && (msg_str[i + length] != ',') && (msg_str[i + length] != '\0')) { length++; } if (length == 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "store_http_digest", NULL, "Length of realm is 0!"); return(INCORRECT); } if (hmr.realm != NULL) { (void)free(hmr.realm); } if ((hmr.realm = malloc(length + 1)) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "store_http_digest", NULL, "Failed to malloc() for HTTP digest realm : %s", strerror(errno)); return(INCORRECT); } (void)memcpy(hmr.realm, &msg_str[i], length); hmr.realm[length] = '\0'; if (msg_str[i + length] == '"') { i += (length + 1); } else { i += length; } } /* nonce= */ else if ((read_length > (i + 6)) && ((msg_str[i] == 'n') || (msg_str[i] == 'N')) && ((msg_str[i + 1] == 'o') || (msg_str[i + 1] == 'O')) && ((msg_str[i + 2] == 'n') || (msg_str[i + 2] == 'N')) && ((msg_str[i + 3] == 'c') || (msg_str[i + 3] == 'C')) && ((msg_str[i + 4] == 'e') || (msg_str[i + 4] == 'E')) && (msg_str[i + 5] == '=') && (msg_str[i + 6] == '"')) { i += 7; length = 0; while ((i < read_length) && (msg_str[i + length] != '"') && (msg_str[i + length] != ',') && (msg_str[i + length] != '\0')) { length++; } if (length == 0) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "store_http_digest", NULL, "Length of nonce is 0!"); return(INCORRECT); } if (hmr.nonce != NULL) { (void)free(hmr.nonce); } if ((hmr.nonce = malloc(length + 1)) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "store_http_digest", NULL, "Failed to malloc() for HTTP digest nonce : %s", strerror(errno)); return(INCORRECT); } (void)memcpy(hmr.nonce, &msg_str[i], length); hmr.nonce[length] = '\0'; if (msg_str[i + length] == '"') { i += (length + 1); } else { i += length; } } /* algorithm= */ else if ((read_length > (i + 9)) && ((msg_str[i] == 'a') || (msg_str[i] == 'A')) && ((msg_str[i + 1] == 'l') || (msg_str[i + 1] == 'L')) && ((msg_str[i + 2] == 'g') || (msg_str[i + 2] == 'G')) && ((msg_str[i + 3] == 'o') || (msg_str[i + 3] == 'O')) && ((msg_str[i + 4] == 'r') || (msg_str[i + 4] == 'R')) && ((msg_str[i + 5] == 'i') || (msg_str[i + 5] == 'I')) && ((msg_str[i + 6] == 't') || (msg_str[i + 6] == 'T')) && ((msg_str[i + 7] == 'h') || (msg_str[i + 7] == 'H')) && ((msg_str[i + 8] == 'm') || (msg_str[i + 8] == 'M')) && (msg_str[i + 9] == '=')) { i += 10; /* MD5 */ if (((i + 3) <= read_length) && ((msg_str[i] == 'M') || (msg_str[i] == 'm')) && ((msg_str[i + 1] == 'D') || (msg_str[i + 1] == 'd')) && ((msg_str[i + 2] == '5') && ((msg_str[i + 3] == ',') || (msg_str[i + 3] == '\0')))) { i += 3; hmr.www_authenticate = WWW_AUTHENTICATE_DIGEST_MD5; } #ifdef HAVE_EVP_SHA256 /* SHA-256 */ else if (((i + 7) <= read_length) && ((msg_str[i] == 'S') || (msg_str[i] == 's')) && ((msg_str[i + 1] == 'H') || (msg_str[i + 1] == 'h')) && ((msg_str[i + 2] == 'A') || (msg_str[i + 2] == 'a')) && ((msg_str[i + 3] == '-') && (msg_str[i + 4] == '2') && (msg_str[i + 5] == '5') && (msg_str[i + 6] == '6') && ((msg_str[i + 7] == ',') || (msg_str[i + 7] == '\0')))) { i += 7; hmr.www_authenticate = WWW_AUTHENTICATE_DIGEST_SHA256; } #endif /* MD5-sess */ else if (((i + 8) <= read_length) && ((msg_str[i] == 'M') || (msg_str[i] == 'm')) && ((msg_str[i + 1] == 'D') || (msg_str[i + 1] == 'd')) && ((msg_str[i + 2] == '5') && (msg_str[i + 3] == '-') && ((msg_str[i + 4] == 's') || (msg_str[i + 4] == 'S')) && ((msg_str[i + 5] == 'e') || (msg_str[i + 5] == 'E')) && ((msg_str[i + 6] == 's') || (msg_str[i + 6] == 'S')) && ((msg_str[i + 7] == 's') || (msg_str[i + 7] == 'S')) && ((msg_str[i + 8] == ',') || (msg_str[i + 8] == '\0')))) { i += 8; hmr.www_authenticate = WWW_AUTHENTICATE_DIGEST_MD5_S; } #ifdef HAVE_EVP_SHA256 /* SHA-256-sess */ else if (((i + 12) <= read_length) && ((msg_str[i] == 'S') || (msg_str[i] == 's')) && ((msg_str[i + 1] == 'H') || (msg_str[i + 1] == 'h')) && ((msg_str[i + 2] == 'A') || (msg_str[i + 2] == 'a')) && ((msg_str[i + 3] == '-') && (msg_str[i + 4] == '2') && (msg_str[i + 5] == '5') && (msg_str[i + 6] == '6') && (msg_str[i + 7] == '-') && ((msg_str[i + 8] == 's') || (msg_str[i + 8] == 'S')) && ((msg_str[i + 9] == 'e') || (msg_str[i + 9] == 'E')) && ((msg_str[i + 10] == 's') || (msg_str[i + 10] == 'S')) && ((msg_str[i + 11] == 's') || (msg_str[i + 11] == 'S')) && ((msg_str[i + 12] == ',') || (msg_str[i + 12] == '\0')))) { i += 12; hmr.www_authenticate = WWW_AUTHENTICATE_DIGEST_SHA256_S; } #endif #ifdef HAVE_EVP_SHA512_256 /* SHA-512-256 */ else if (((i + 11) <= read_length) && ((msg_str[i] == 'S') || (msg_str[i] == 's')) && ((msg_str[i + 1] == 'H') || (msg_str[i + 1] == 'h')) && ((msg_str[i + 2] == 'A') || (msg_str[i + 2] == 'a')) && ((msg_str[i + 3] == '-') && (msg_str[i + 4] == '5') && (msg_str[i + 5] == '1') && (msg_str[i + 6] == '2') && (msg_str[i + 7] == '-') && (msg_str[i + 8] == '2') && (msg_str[i + 9] == '5') && (msg_str[i + 10] == '6') && ((msg_str[i + 11] == ',') || (msg_str[i + 11] == '\0')))) { i += 11; hmr.www_authenticate = WWW_AUTHENTICATE_DIGEST_SHA512_256; } /* SHA-512-256-sees */ else if (((i + 17) <= read_length) && ((msg_str[i] == 'S') || (msg_str[i] == 's')) && ((msg_str[i + 1] == 'H') || (msg_str[i + 1] == 'h')) && ((msg_str[i + 2] == 'A') || (msg_str[i + 2] == 'a')) && ((msg_str[i + 3] == '-') && (msg_str[i + 4] == '5') && (msg_str[i + 5] == '1') && (msg_str[i + 6] == '2') && (msg_str[i + 7] == '-') && (msg_str[i + 8] == '2') && (msg_str[i + 9] == '5') && (msg_str[i + 10] == '6') && (msg_str[i + 11] == '-') && ((msg_str[i + 12] == 's') || (msg_str[i + 12] == 'S')) && ((msg_str[i + 13] == 'e') || (msg_str[i + 13] == 'E')) && ((msg_str[i + 14] == 's') || (msg_str[i + 14] == 'S')) && ((msg_str[i + 15] == 's') || (msg_str[i + 15] == 'S')) && ((msg_str[i + 16] == ',') || (msg_str[i + 16] == '\0')) && ((msg_str[i + 17] == ',') || (msg_str[i + 17] == '\0')))) { i += 17; hmr.www_authenticate = WWW_AUTHENTICATE_DIGEST_SHA512_256_S; } #endif else { int j = 0; char unknown[MAX_RET_MSG_LENGTH]; /* Unknown */ while ((i < read_length) && (msg_str[i] != ',') && (msg_str[i] != '\0')) { unknown[j] = msg_str[i]; i++; j++; } unknown[j] = '\0'; hmr.www_authenticate = WWW_AUTHENTICATE_DIGEST_UNKNOWN; trans_log(ERROR_SIGN, __FILE__, __LINE__, "store_http_digest", NULL, "Unknown HTTP digest algorithm : %s", unknown); return(INCORRECT); } } /* qop= */ else if ((read_length > (i + 4)) && ((msg_str[i] == 'q') || (msg_str[i] == 'Q')) && ((msg_str[i + 1] == 'o') || (msg_str[i + 1] == 'O')) && ((msg_str[i + 2] == 'p') || (msg_str[i + 2] == 'P')) && (msg_str[i + 3] == '=') && (msg_str[i + 4] == '"')) { i += 5; if (((i + 4) <= read_length) && ((msg_str[i] == 'a') || (msg_str[i] == 'A')) && ((msg_str[i + 1] == 'u') || (msg_str[i + 1] == 'U')) && ((msg_str[i + 2] == 't') || (msg_str[i + 2] == 'T')) && ((msg_str[i + 3] == 'h') || (msg_str[i + 3] == 'H')) && ((msg_str[i + 4] == '"') || (msg_str[i + 4] == ','))) { hmr.digest_options |= QOP_AUTH; if (msg_str[i + 4] == '"') { i += 5; } else { i += 5; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; /* Ignore space. */ } if (((i + 8) <= read_length) && ((msg_str[i] == 'a') || (msg_str[i] == 'A')) && ((msg_str[i + 1] == 'u') || (msg_str[i + 1] == 'U')) && ((msg_str[i + 2] == 't') || (msg_str[i + 2] == 'T')) && ((msg_str[i + 3] == 'h') || (msg_str[i + 3] == 'H')) && (msg_str[i + 4] == '-') && ((msg_str[i + 5] == 'i') || (msg_str[i + 5] == 'I')) && ((msg_str[i + 6] == 'n') || (msg_str[i + 6] == 'N')) && ((msg_str[i + 7] == 't') || (msg_str[i + 7] == 'T')) && ((msg_str[i + 8] == '"') || (msg_str[i + 8] == ','))) { hmr.digest_options |= QOP_AUTH_INT; if (msg_str[i + 8] == '"') { i += 9; } else { /* Some unknown quality of protection, lets ignore. */ i += 9; while ((i < read_length) && (msg_str[i] != '"') && (msg_str[i] != '\0')) { i++; } if (msg_str[i] == '"') { i++; } } } else { /* Some unknown quality of protection, lets ignore. */ while ((i < read_length) && (msg_str[i] != '"') && (msg_str[i] != '\0')) { i++; } if (msg_str[i] == '"') { i++; } } } } else if (((i + 8) <= read_length) && ((msg_str[i] == 'a') || (msg_str[i] == 'A')) && ((msg_str[i + 1] == 'u') || (msg_str[i + 1] == 'U')) && ((msg_str[i + 2] == 't') || (msg_str[i + 2] == 'T')) && ((msg_str[i + 3] == 'h') || (msg_str[i + 3] == 'H')) && (msg_str[i + 4] == '-') && ((msg_str[i + 5] == 'i') || (msg_str[i + 5] == 'I')) && ((msg_str[i + 6] == 'n') || (msg_str[i + 6] == 'N')) && ((msg_str[i + 7] == 't') || (msg_str[i + 7] == 'T')) && ((msg_str[i + 8] == '"') || (msg_str[i + 8] == ','))) { hmr.digest_options |= QOP_AUTH_INT; if (msg_str[i + 8] == '"') { i += 9; } else { i += 9; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; /* Ignore space. */ } if (((i + 4) <= read_length) && ((msg_str[i] == 'a') || (msg_str[i] == 'A')) && ((msg_str[i + 1] == 'u') || (msg_str[i + 1] == 'U')) && ((msg_str[i + 2] == 't') || (msg_str[i + 2] == 'T')) && ((msg_str[i + 3] == 'h') || (msg_str[i + 3] == 'H')) && ((msg_str[i + 4] == '"') || (msg_str[i + 4] == ','))) { hmr.digest_options |= QOP_AUTH; if (msg_str[i + 4] == '"') { i += 5; } else { /* Some unknown quality of protection, */ /* lets ignore. */ i += 5; while ((i < read_length) && (msg_str[i] != '"') && (msg_str[i] != '\0')) { i++; } if (msg_str[i] == '"') { i++; } } } else { /* Some unknown quality of protection, */ /* lets ignore. */ i += 5; while ((i < read_length) && (msg_str[i] != '"') && (msg_str[i] != '\0')) { i++; } if (msg_str[i] == '"') { i++; } } } } else { /* Some unknown quality of protection, lets ignore. */ while ((i < read_length) && (msg_str[i] != '"') && (msg_str[i] != '\0')) { i++; } if (msg_str[i] == '"') { i++; } } } /* userhash=true */ else if (((i + 13) <= read_length) && ((msg_str[i] == 'u') || (msg_str[i] == 'U')) && ((msg_str[i + 1] == 's') || (msg_str[i + 1] == 'S')) && ((msg_str[i + 2] == 'e') || (msg_str[i + 2] == 'E')) && ((msg_str[i + 3] == 'r') || (msg_str[i + 3] == 'R')) && ((msg_str[i + 4] == 'h') || (msg_str[i + 4] == 'H')) && ((msg_str[i + 5] == 'a') || (msg_str[i + 5] == 'A')) && ((msg_str[i + 6] == 's') || (msg_str[i + 6] == 'S')) && ((msg_str[i + 7] == 'h') || (msg_str[i + 7] == 'H')) && (msg_str[i + 8] == '=') && ((msg_str[i + 9] == 't') || (msg_str[i + 9] == 'T')) && ((msg_str[i + 10] == 'r') || (msg_str[i + 10] == 'R')) && ((msg_str[i + 11] == 'u') || (msg_str[i + 11] == 'U')) && ((msg_str[i + 12] == 'e') || (msg_str[i + 12] == 'E')) && ((msg_str[i + 13] == ',') || (msg_str[i + 13] == '\0'))) { i += 13; hmr.digest_options |= HASH_USERNAME; } /* opaque= */ else if ((read_length > (i + 7)) && ((msg_str[i] == 'o') || (msg_str[i] == 'O')) && ((msg_str[i + 1] == 'p') || (msg_str[i + 1] == 'P')) && ((msg_str[i + 2] == 'a') || (msg_str[i + 2] == 'A')) && ((msg_str[i + 3] == 'q') || (msg_str[i + 3] == 'Q')) && ((msg_str[i + 4] == 'u') || (msg_str[i + 4] == 'U')) && ((msg_str[i + 5] == 'e') || (msg_str[i + 5] == 'E')) && (msg_str[i + 6] == '=') && (msg_str[i + 7] == '"')) { i += 8; length = 0; while ((i < read_length) && (msg_str[i + length] != '"') && (msg_str[i + length] != ',') && (msg_str[i + length] != '\0')) { length++; } if (hmr.opaque != NULL) { (void)free(hmr.opaque); } if ((hmr.opaque = malloc(length + 1)) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "store_http_digest", NULL, "Failed to malloc() for HTTP digest opaque : %s", strerror(errno)); return(INCORRECT); } if (length > 0) { (void)memcpy(hmr.opaque, &msg_str[i], length); } hmr.opaque[length] = '\0'; if (msg_str[i + length] == '"') { i += (length + 1); } else { i += length; } } /* nextnonce= */ else if ((read_length > (i + 6)) && ((msg_str[i] == 'n') || (msg_str[i] == 'N')) && ((msg_str[i + 1] == 'e') || (msg_str[i + 1] == 'E')) && ((msg_str[i + 2] == 'x') || (msg_str[i + 2] == 'X')) && ((msg_str[i + 3] == 't') || (msg_str[i + 3] == 'T')) && ((msg_str[i + 4] == 'n') || (msg_str[i + 4] == 'N')) && ((msg_str[i + 5] == 'o') || (msg_str[i + 5] == 'O')) && ((msg_str[i + 6] == 'n') || (msg_str[i + 6] == 'N')) && ((msg_str[i + 7] == 'c') || (msg_str[i + 7] == 'C')) && ((msg_str[i + 8] == 'e') || (msg_str[i + 8] == 'E')) && (msg_str[i + 9] == '=') && (msg_str[i + 10] == '"')) { i += 11; length = 0; while ((i < read_length) && (msg_str[i + length] != '"') && (msg_str[i + length] != ',') && (msg_str[i + length] != '\0')) { length++; } if (length == 0) { trans_log(DEBUG_SIGN, __FILE__, __LINE__, "store_http_digest", NULL, "Length of nextnonce is 0!"); if (hmr.nonce != NULL) { hmr.nonce[length] = '\0'; } } else { if (hmr.nonce != NULL) { (void)free(hmr.nonce); } if ((hmr.nonce = malloc(length + 1)) == NULL) { trans_log(ERROR_SIGN, __FILE__, __LINE__, "store_http_digest", NULL, "Failed to malloc() for HTTP digest nonce : %s", strerror(errno)); return(INCORRECT); } (void)memcpy(hmr.nonce, &msg_str[i], length); hmr.nonce[length] = '\0'; } if (msg_str[i + length] == '"') { i += (length + 1); } else { i += length; } } else /* Unknown field, ignore it. */ { while ((i < read_length) && (msg_str[i] != ',') && (msg_str[i] != '\0')) { i++; } } if (msg_str[i] == ',') { i++; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } } } return(SUCCESS); } /*------------------------- store_http_options() ------------------------*/ static void store_http_options(int i, int read_length) { while ((i < read_length) && (msg_str[i] != '\0')) { /* HEAD */ if (((i + 4) <= read_length) && ((msg_str[i] == 'H') || (msg_str[i] == 'h')) && ((msg_str[i + 1] == 'E') || (msg_str[i + 1] == 'e')) && ((msg_str[i + 2] == 'A') || (msg_str[i + 2] == 'a')) && ((msg_str[i + 3] == 'D') || (msg_str[i + 3] == 'd')) && ((msg_str[i + 4] == ',') || (msg_str[i + 4] == '\0'))) { hmr.http_options |= HTTP_OPTION_HEAD; i += 4; } /* GET */ else if (((i + 3) <= read_length) && ((msg_str[i] == 'G') || (msg_str[i] == 'g')) && ((msg_str[i + 1] == 'E') || (msg_str[i + 1] == 'e')) && ((msg_str[i + 2] == 'T') || (msg_str[i + 2] == 't')) && ((msg_str[i + 3] == ',') || (msg_str[i + 3] == '\0'))) { hmr.http_options |= HTTP_OPTION_GET; i += 3; } /* PUT */ else if (((i + 3) <= read_length) && ((msg_str[i] == 'P') || (msg_str[i] == 'p')) && ((msg_str[i + 1] == 'U') || (msg_str[i + 1] == 'u')) && ((msg_str[i + 2] == 'T') || (msg_str[i + 2] == 't')) && ((msg_str[i + 3] == ',') || (msg_str[i + 3] == '\0'))) { hmr.http_options |= HTTP_OPTION_PUT; i += 3; } /* MOVE */ else if (((i + 4) <= read_length) && ((msg_str[i] == 'M') || (msg_str[i] == 'm')) && ((msg_str[i + 1] == 'O') || (msg_str[i + 1] == 'o')) && ((msg_str[i + 2] == 'V') || (msg_str[i + 2] == 'v')) && ((msg_str[i + 3] == 'E') || (msg_str[i + 3] == 'e')) && ((msg_str[i + 4] == ',') || (msg_str[i + 4] == '\0'))) { hmr.http_options |= HTTP_OPTION_MOVE; i += 4; } /* POST */ else if (((i + 4) <= read_length) && ((msg_str[i] == 'P') || (msg_str[i] == 'p')) && ((msg_str[i + 1] == 'O') || (msg_str[i + 1] == 'o')) && ((msg_str[i + 2] == 'S') || (msg_str[i + 2] == 's')) && ((msg_str[i + 3] == 'T') || (msg_str[i + 3] == 't')) && ((msg_str[i + 4] == ',') || (msg_str[i + 4] == '\0'))) { hmr.http_options |= HTTP_OPTION_POST; i += 4; } /* DELETE */ else if (((i + 6) <= read_length) && ((msg_str[i] == 'D') || (msg_str[i] == 'd')) && ((msg_str[i + 1] == 'E') || (msg_str[i + 1] == 'e')) && ((msg_str[i + 2] == 'L') || (msg_str[i + 2] == 'l')) && ((msg_str[i + 3] == 'E') || (msg_str[i + 3] == 'e')) && ((msg_str[i + 4] == 'T') || (msg_str[i + 4] == 't')) && ((msg_str[i + 5] == 'E') || (msg_str[i + 5] == 'e')) && ((msg_str[i + 6] == ',') || (msg_str[i + 6] == '\0'))) { hmr.http_options |= HTTP_OPTION_DELETE; i += 6; } /* OPTIONS */ else if (((i + 7) <= read_length) && ((msg_str[i] == 'O') || (msg_str[i] == 'o')) && ((msg_str[i + 1] == 'P') || (msg_str[i + 1] == 'p')) && ((msg_str[i + 2] == 'T') || (msg_str[i + 2] == 't')) && ((msg_str[i + 3] == 'I') || (msg_str[i + 3] == 'i')) && ((msg_str[i + 4] == 'O') || (msg_str[i + 4] == 'o')) && ((msg_str[i + 5] == 'N') || (msg_str[i + 5] == 'n')) && ((msg_str[i + 6] == 'S') || (msg_str[i + 6] == 's')) && ((msg_str[i + 7] == ',') || (msg_str[i + 7] == '\0'))) { hmr.http_options |= HTTP_OPTION_OPTIONS; i += 7; } else /* Ignore any other options. */ { while ((i < read_length) && (msg_str[i] != ',') && (msg_str[i] != '\0')) { i++; } } if (msg_str[i] == ',') { i++; while ((i < read_length) && ((msg_str[i] == ' ') || (msg_str[i] == '\t'))) { i++; } } } return; } #ifdef WITH_SSL /*+++++++++++++++++++++++++++++ sig_handler() +++++++++++++++++++++++++++*/ static void sig_handler(int signo) { siglongjmp(env_alrm, 1); } #endif