/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * ***************************************************************************/ /* OS/400 additional support. */ #include "curlbuild.h" #include "config-os400.h" /* Not setup.h: we only need some defines. */ #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <stdlib.h> #include <stddef.h> #include <string.h> #include <pthread.h> #include <netdb.h> #include <qadrt.h> #include <errno.h> #ifdef USE_QSOSSL #include <qsossl.h> #endif #ifdef HAVE_GSSAPI #include <gssapi.h> #endif #ifndef CURL_DISABLE_LDAP #include <ldap.h> #endif #include <netinet/in.h> #include <arpa/inet.h> #include "os400sys.h" /** *** QADRT OS/400 ASCII runtime defines only the most used procedures, but *** but a lot of them are not supported. This module implements *** ASCII wrappers for those that are used by libcurl, but not *** defined by QADRT. **/ #pragma convert(0) /* Restore EBCDIC. */ #define MIN_BYTE_GAIN 1024 /* Minimum gain when shortening a buffer. */ typedef struct { unsigned long size; /* Buffer size. */ char * buf; /* Buffer address. */ } buffer_t; static char * buffer_undef(localkey_t key, long size); static char * buffer_threaded(localkey_t key, long size); static char * buffer_unthreaded(localkey_t key, long size); static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_key_t thdkey; static buffer_t * locbufs; char * (* Curl_thread_buffer)(localkey_t key, long size) = buffer_undef; static void thdbufdestroy(void * private) { localkey_t i; buffer_t * p; if (private) { p = (buffer_t *) private; for (i = (localkey_t) 0; i < LK_LAST; i++) { if (p->buf) free(p->buf); p++; } free(private); } } static void terminate(void) { if (Curl_thread_buffer == buffer_threaded) { locbufs = pthread_getspecific(thdkey); pthread_setspecific(thdkey, (void *) NULL); pthread_key_delete(thdkey); } if (Curl_thread_buffer != buffer_undef) { thdbufdestroy((void *) locbufs); locbufs = (buffer_t *) NULL; } Curl_thread_buffer = buffer_undef; } static char * get_buffer(buffer_t * buf, long size) { char * cp; /* If `size' >= 0, make sure buffer at `buf' is at least `size'-byte long. Return the buffer address. */ if (size < 0) return buf->buf; if (!buf->buf) { if ((buf->buf = malloc(size))) buf->size = size; return buf->buf; } if ((unsigned long) size <= buf->size) { /* Shorten the buffer only if it frees a significant byte count. This avoids some realloc() overhead. */ if (buf->size - size < MIN_BYTE_GAIN) return buf->buf; } /* Resize the buffer. */ if ((cp = realloc(buf->buf, size))) { buf->buf = cp; buf->size = size; } else if (size <= buf->size) cp = buf->buf; return cp; } static char * buffer_unthreaded(localkey_t key, long size) { return get_buffer(locbufs + key, size); } static char * buffer_threaded(localkey_t key, long size) { buffer_t * bufs; /* Get the buffer for the given local key in the current thread, and make sure it is at least `size'-byte long. Set `size' to < 0 to get its address only. */ bufs = (buffer_t *) pthread_getspecific(thdkey); if (!bufs) { if (size < 0) return (char *) NULL; /* No buffer yet. */ /* Allocate buffer descriptors for the current thread. */ if (!(bufs = calloc((size_t) LK_LAST, sizeof *bufs))) return (char *) NULL; if (pthread_setspecific(thdkey, (void *) bufs)) { free(bufs); return (char *) NULL; } } return get_buffer(bufs + key, size); } static char * buffer_undef(localkey_t key, long size) { /* Define the buffer system, get the buffer for the given local key in the current thread, and make sure it is at least `size'-byte long. Set `size' to < 0 to get its address only. */ pthread_mutex_lock(&mutex); /* Determine if we can use pthread-specific data. */ if (Curl_thread_buffer == buffer_undef) { /* If unchanged during lock. */ if (!pthread_key_create(&thdkey, thdbufdestroy)) Curl_thread_buffer = buffer_threaded; else if (!(locbufs = calloc((size_t) LK_LAST, sizeof *locbufs))) { pthread_mutex_unlock(&mutex); return (char *) NULL; } else Curl_thread_buffer = buffer_unthreaded; atexit(terminate); } pthread_mutex_unlock(&mutex); return Curl_thread_buffer(key, size); } int Curl_getnameinfo_a(const struct sockaddr * sa, curl_socklen_t salen, char * nodename, curl_socklen_t nodenamelen, char * servname, curl_socklen_t servnamelen, int flags) { char * enodename; char * eservname; int status; int i; enodename = (char *) NULL; eservname = (char *) NULL; if (nodename && nodenamelen) if (!(enodename = malloc(nodenamelen))) return EAI_MEMORY; if (servname && servnamelen) if (!(eservname = malloc(servnamelen))) { if (enodename) free(enodename); return EAI_MEMORY; } status = getnameinfo(sa, salen, enodename, nodenamelen, eservname, servnamelen, flags); if (!status) { if (enodename) { i = QadrtConvertE2A(nodename, enodename, nodenamelen - 1, strlen(enodename)); nodename[i] = '\0'; } if (eservname) { i = QadrtConvertE2A(servname, eservname, servnamelen - 1, strlen(eservname)); servname[i] = '\0'; } } if (enodename) free(enodename); if (eservname) free(eservname); return status; } int Curl_getaddrinfo_a(const char * nodename, const char * servname, const struct addrinfo * hints, struct addrinfo * * res) { char * enodename; char * eservname; int status; int i; enodename = (char *) NULL; eservname = (char *) NULL; if (nodename) { i = strlen(nodename); if (!(enodename = malloc(i + 1))) return EAI_MEMORY; i = QadrtConvertA2E(enodename, nodename, i, i); enodename[i] = '\0'; } if (servname) { i = strlen(servname); if (!(eservname = malloc(i + 1))) { if (enodename) free(enodename); return EAI_MEMORY; } QadrtConvertA2E(eservname, servname, i, i); eservname[i] = '\0'; } status = getaddrinfo(enodename, eservname, hints, res); if (enodename) free(enodename); if (eservname) free(eservname); return status; } #ifdef USE_QSOSSL /* ASCII wrappers for the SSL procedures. */ int Curl_SSL_Init_Application_a(SSLInitApp * init_app) { int rc; unsigned int i; SSLInitApp ia; if (!init_app || !init_app->applicationID || !init_app->applicationIDLen) return SSL_Init_Application(init_app); memcpy((char *) &ia, (char *) init_app, sizeof ia); i = ia.applicationIDLen; if (!(ia.applicationID = malloc(i + 1))) { errno = ENOMEM; return SSL_ERROR_IO; } QadrtConvertA2E(ia.applicationID, init_app->applicationID, i, i); ia.applicationID[i] = '\0'; rc = SSL_Init_Application(&ia); free(ia.applicationID); init_app->localCertificateLen = ia.localCertificateLen; init_app->sessionType = ia.sessionType; return rc; } int Curl_SSL_Init_a(SSLInit * init) { int rc; unsigned int i; SSLInit ia; if (!init || (!init->keyringFileName && !init->keyringPassword)) return SSL_Init(init); memcpy((char *) &ia, (char *) init, sizeof ia); if (ia.keyringFileName) { i = strlen(ia.keyringFileName); if (!(ia.keyringFileName = malloc(i + 1))) { errno = ENOMEM; return SSL_ERROR_IO; } QadrtConvertA2E(ia.keyringFileName, init->keyringFileName, i, i); ia.keyringFileName[i] = '\0'; } if (ia.keyringPassword) { i = strlen(ia.keyringPassword); if (!(ia.keyringPassword = malloc(i + 1))) { if (ia.keyringFileName) free(ia.keyringFileName); errno = ENOMEM; return SSL_ERROR_IO; } QadrtConvertA2E(ia.keyringPassword, init->keyringPassword, i, i); ia.keyringPassword[i] = '\0'; } rc = SSL_Init(&ia); if (ia.keyringFileName) free(ia.keyringFileName); if (ia.keyringPassword) free(ia.keyringPassword); return rc; } char * Curl_SSL_Strerror_a(int sslreturnvalue, SSLErrorMsg * serrmsgp) { int i; char * cp; char * cp2; cp = SSL_Strerror(sslreturnvalue, serrmsgp); if (!cp) return cp; i = strlen(cp); if (!(cp2 = Curl_thread_buffer(LK_SSL_ERROR, MAX_CONV_EXPANSION * i + 1))) return cp2; i = QadrtConvertE2A(cp2, cp, MAX_CONV_EXPANSION * i, i); cp2[i] = '\0'; return cp2; } #endif /* USE_QSOSSL */ #ifdef HAVE_GSSAPI /* ASCII wrappers for the GSSAPI procedures. */ static int Curl_gss_convert_in_place(OM_uint32 * minor_status, gss_buffer_t buf) { unsigned int i; char * t; /* Convert `buf' in place, from EBCDIC to ASCII. If error, release the buffer and return -1. Else return 0. */ i = buf->length; if (i) { if (!(t = malloc(i))) { gss_release_buffer(minor_status, buf); if (minor_status) *minor_status = ENOMEM; return -1; } QadrtConvertE2A(t, buf->value, i, i); memcpy(buf->value, t, i); free(t); } return 0; } OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, gss_buffer_t in_name, gss_OID in_name_type, gss_name_t * out_name) { int rc; unsigned int i; gss_buffer_desc in; if (!in_name || !in_name->value || !in_name->length) return gss_import_name(minor_status, in_name, in_name_type, out_name); memcpy((char *) &in, (char *) in_name, sizeof in); i = in.length; if (!(in.value = malloc(i + 1))) { if (minor_status) *minor_status = ENOMEM; return GSS_S_FAILURE; } QadrtConvertA2E(in.value, in_name->value, i, i); ((char *) in.value)[i] = '\0'; rc = gss_import_name(minor_status, &in, in_name_type, out_name); free(in.value); return rc; } OM_uint32 Curl_gss_display_status_a(OM_uint32 * minor_status, OM_uint32 status_value, int status_type, gss_OID mech_type, gss_msg_ctx_t * message_context, gss_buffer_t status_string) { int rc; rc = gss_display_status(minor_status, status_value, status_type, mech_type, message_context, status_string); if (rc != GSS_S_COMPLETE || !status_string || !status_string->length || !status_string->value) return rc; /* No way to allocate a buffer here, because it will be released by gss_release_buffer(). The solution is to overwrite the EBCDIC buffer with ASCII to return it. */ if (Curl_gss_convert_in_place(minor_status, status_string)) return GSS_S_FAILURE; return rc; } OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 * minor_status, gss_cred_id_t cred_handle, gss_ctx_id_t * context_handle, gss_name_t target_name, gss_OID mech_type, gss_flags_t req_flags, OM_uint32 time_req, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, gss_flags_t * ret_flags, OM_uint32 * time_rec) { int rc; unsigned int i; gss_buffer_desc in; gss_buffer_t inp; in.value = NULL; if ((inp = input_token)) if (inp->length && inp->value) { i = inp->length; if (!(in.value = malloc(i + 1))) { if (minor_status) *minor_status = ENOMEM; return GSS_S_FAILURE; } QadrtConvertA2E(in.value, input_token->value, i, i); ((char *) in.value)[i] = '\0'; in.length = i; inp = ∈ } rc = gss_init_sec_context(minor_status, cred_handle, context_handle, target_name, mech_type, req_flags, time_req, input_chan_bindings, inp, actual_mech_type, output_token, ret_flags, time_rec); if (in.value) free(in.value); if (rc != GSS_S_COMPLETE || !output_token || !output_token->length || !output_token->value) return rc; /* No way to allocate a buffer here, because it will be released by gss_release_buffer(). The solution is to overwrite the EBCDIC buffer with ASCII to return it. */ if (Curl_gss_convert_in_place(minor_status, output_token)) return GSS_S_FAILURE; return rc; } OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status, gss_ctx_id_t * context_handle, gss_buffer_t output_token) { int rc; rc = gss_delete_sec_context(minor_status, context_handle, output_token); if (rc != GSS_S_COMPLETE || !output_token || !output_token->length || !output_token->value) return rc; /* No way to allocate a buffer here, because it will be released by gss_release_buffer(). The solution is to overwrite the EBCDIC buffer with ASCII to return it. */ if (Curl_gss_convert_in_place(minor_status, output_token)) return GSS_S_FAILURE; return rc; } #endif /* HAVE_GSSAPI */ #ifndef CURL_DISABLE_LDAP /* ASCII wrappers for the LDAP procedures. */ void * Curl_ldap_init_a(char * host, int port) { unsigned int i; char * ehost; void * result; if (!host) return (void *) ldap_init(host, port); i = strlen(host); if (!(ehost = malloc(i + 1))) return (void *) NULL; QadrtConvertA2E(ehost, host, i, i); ehost[i] = '\0'; result = (void *) ldap_init(ehost, port); free(ehost); return result; } int Curl_ldap_simple_bind_s_a(void * ld, char * dn, char * passwd) { int i; char * edn; char * epasswd; edn = (char *) NULL; epasswd = (char *) NULL; if (dn) { i = strlen(dn); if (!(edn = malloc(i + 1))) return LDAP_NO_MEMORY; QadrtConvertA2E(edn, dn, i, i); edn[i] = '\0'; } if (passwd) { i = strlen(passwd); if (!(epasswd = malloc(i + 1))) { if (edn) free(edn); return LDAP_NO_MEMORY; } QadrtConvertA2E(epasswd, passwd, i, i); epasswd[i] = '\0'; } i = ldap_simple_bind_s(ld, edn, epasswd); if (epasswd) free(epasswd); if (edn) free(edn); return i; } int Curl_ldap_search_s_a(void * ld, char * base, int scope, char * filter, char * * attrs, int attrsonly, LDAPMessage * * res) { int i; int j; char * ebase; char * efilter; char * * eattrs; int status; ebase = (char *) NULL; efilter = (char *) NULL; eattrs = (char * *) NULL; status = LDAP_SUCCESS; if (base) { i = strlen(base); if (!(ebase = malloc(i + 1))) status = LDAP_NO_MEMORY; else { QadrtConvertA2E(ebase, base, i, i); ebase[i] = '\0'; } } if (filter && status == LDAP_SUCCESS) { i = strlen(filter); if (!(efilter = malloc(i + 1))) status = LDAP_NO_MEMORY; else { QadrtConvertA2E(efilter, filter, i, i); efilter[i] = '\0'; } } if (attrs && status == LDAP_SUCCESS) { for (i = 0; attrs[i++];) ; if (!(eattrs = calloc(i, sizeof *eattrs))) status = LDAP_NO_MEMORY; else { for (j = 0; attrs[j]; j++) { i = strlen(attrs[j]); if (!(eattrs[j] = malloc(i + 1))) { status = LDAP_NO_MEMORY; break; } QadrtConvertA2E(eattrs[j], attrs[j], i, i); eattrs[j][i] = '\0'; } } } if (status == LDAP_SUCCESS) status = ldap_search_s(ld, ebase? ebase: "", scope, efilter? efilter: "(objectclass=*)", eattrs, attrsonly, res); if (eattrs) { for (j = 0; eattrs[j]; j++) free(eattrs[j]); free(eattrs); } if (efilter) free(efilter); if (ebase) free(ebase); return status; } struct berval * * Curl_ldap_get_values_len_a(void * ld, LDAPMessage * entry, const char * attr) { int i; char * cp; struct berval * * result; cp = (char *) NULL; if (attr) { i = strlen(attr); if (!(cp = malloc(i + 1))) { ldap_set_lderrno(ld, LDAP_NO_MEMORY, NULL, ldap_err2string(LDAP_NO_MEMORY)); return (struct berval * *) NULL; } QadrtConvertA2E(cp, attr, i, i); cp[i] = '\0'; } result = ldap_get_values_len(ld, entry, cp); if (cp) free(cp); /* Result data are binary in nature, so they haven't been converted to EBCDIC. Therefore do not convert. */ return result; } char * Curl_ldap_err2string_a(int error) { int i; char * cp; char * cp2; cp = ldap_err2string(error); if (!cp) return cp; i = strlen(cp); if (!(cp2 = Curl_thread_buffer(LK_LDAP_ERROR, MAX_CONV_EXPANSION * i + 1))) return cp2; i = QadrtConvertE2A(cp2, cp, MAX_CONV_EXPANSION * i, i); cp2[i] = '\0'; return cp2; } char * Curl_ldap_get_dn_a(void * ld, LDAPMessage * entry) { int i; char * cp; char * cp2; cp = ldap_get_dn(ld, entry); if (!cp) return cp; i = strlen(cp); if (!(cp2 = malloc(i + 1))) return cp2; QadrtConvertE2A(cp2, cp, i, i); cp2[i] = '\0'; /* No way to allocate a buffer here, because it will be released by ldap_memfree() and ldap_memalloc() does not exist. The solution is to overwrite the EBCDIC buffer with ASCII to return it. */ strcpy(cp, cp2); free(cp2); return cp; } char * Curl_ldap_first_attribute_a(void * ld, LDAPMessage * entry, BerElement * * berptr) { int i; char * cp; char * cp2; cp = ldap_first_attribute(ld, entry, berptr); if (!cp) return cp; i = strlen(cp); if (!(cp2 = malloc(i + 1))) return cp2; QadrtConvertE2A(cp2, cp, i, i); cp2[i] = '\0'; /* No way to allocate a buffer here, because it will be released by ldap_memfree() and ldap_memalloc() does not exist. The solution is to overwrite the EBCDIC buffer with ASCII to return it. */ strcpy(cp, cp2); free(cp2); return cp; } char * Curl_ldap_next_attribute_a(void * ld, LDAPMessage * entry, BerElement * berptr) { int i; char * cp; char * cp2; cp = ldap_next_attribute(ld, entry, berptr); if (!cp) return cp; i = strlen(cp); if (!(cp2 = malloc(i + 1))) return cp2; QadrtConvertE2A(cp2, cp, i, i); cp2[i] = '\0'; /* No way to allocate a buffer here, because it will be released by ldap_memfree() and ldap_memalloc() does not exist. The solution is to overwrite the EBCDIC buffer with ASCII to return it. */ strcpy(cp, cp2); free(cp2); return cp; } #endif /* CURL_DISABLE_LDAP */ static int convert_sockaddr(struct sockaddr_storage * dstaddr, const struct sockaddr * srcaddr, int srclen) { const struct sockaddr_un * srcu; struct sockaddr_un * dstu; unsigned int i; unsigned int dstsize; /* Convert a socket address into job CCSID, if needed. */ if (!srcaddr || srclen < offsetof(struct sockaddr, sa_family) + sizeof srcaddr->sa_family || srclen > sizeof *dstaddr) { errno = EINVAL; return -1; } memcpy((char *) dstaddr, (char *) srcaddr, srclen); switch (srcaddr->sa_family) { case AF_UNIX: srcu = (const struct sockaddr_un *) srcaddr; dstu = (struct sockaddr_un *) dstaddr; dstsize = sizeof *dstaddr - offsetof(struct sockaddr_un, sun_path); srclen -= offsetof(struct sockaddr_un, sun_path); i = QadrtConvertA2E(dstu->sun_path, srcu->sun_path, dstsize - 1, srclen); dstu->sun_path[i] = '\0'; i += offsetof(struct sockaddr_un, sun_path); srclen = i; } return srclen; } int Curl_os400_connect(int sd, struct sockaddr * destaddr, int addrlen) { int i; struct sockaddr_storage laddr; i = convert_sockaddr(&laddr, destaddr, addrlen); if (i < 0) return -1; return connect(sd, (struct sockaddr *) &laddr, i); } int Curl_os400_bind(int sd, struct sockaddr * localaddr, int addrlen) { int i; struct sockaddr_storage laddr; i = convert_sockaddr(&laddr, localaddr, addrlen); if (i < 0) return -1; return bind(sd, (struct sockaddr *) &laddr, i); } int Curl_os400_sendto(int sd, char * buffer, int buflen, int flags, struct sockaddr * dstaddr, int addrlen) { int i; struct sockaddr_storage laddr; i = convert_sockaddr(&laddr, dstaddr, addrlen); if (i < 0) return -1; return sendto(sd, buffer, buflen, flags, (struct sockaddr *) &laddr, i); } int Curl_os400_recvfrom(int sd, char * buffer, int buflen, int flags, struct sockaddr * fromaddr, int * addrlen) { int i; int rcvlen; int laddrlen; const struct sockaddr_un * srcu; struct sockaddr_un * dstu; struct sockaddr_storage laddr; if (!fromaddr || !addrlen || *addrlen <= 0) return recvfrom(sd, buffer, buflen, flags, fromaddr, addrlen); laddrlen = sizeof laddr; laddr.ss_family = AF_UNSPEC; /* To detect if unused. */ rcvlen = recvfrom(sd, buffer, buflen, flags, (struct sockaddr *) &laddr, &laddrlen); if (rcvlen < 0) return rcvlen; switch (laddr.ss_family) { case AF_UNIX: srcu = (const struct sockaddr_un *) &laddr; dstu = (struct sockaddr_un *) fromaddr; i = *addrlen - offsetof(struct sockaddr_un, sun_path); laddrlen -= offsetof(struct sockaddr_un, sun_path); i = QadrtConvertE2A(dstu->sun_path, srcu->sun_path, i, laddrlen); laddrlen = i + offsetof(struct sockaddr_un, sun_path); if (laddrlen < *addrlen) dstu->sun_path[i] = '\0'; break; case AF_UNSPEC: break; default: if (laddrlen > *addrlen) laddrlen = *addrlen; if (laddrlen) memcpy((char *) fromaddr, (char *) &laddr, laddrlen); break; } *addrlen = laddrlen; return rcvlen; }