| /*************************************************************************** | 
 |  *                                  _   _ ____  _ | 
 |  *  Project                     ___| | | |  _ \| | | 
 |  *                             / __| | | | |_) | | | 
 |  *                            | (__| |_| |  _ <| |___ | 
 |  *                             \___|\___/|_| \_\_____| | 
 |  * | 
 |  * Copyright (C) 1998 - 2022, 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 https://curl.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. | 
 |  * | 
 |  * SPDX-License-Identifier: curl | 
 |  * | 
 |  * | 
 |  ***************************************************************************/ | 
 |  | 
 | /* OS/400 additional support. */ | 
 |  | 
 | #include <curl/curl.h> | 
 | #include "config-os400.h"  /* Not curl_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 HAVE_LIBZ | 
 | #include <zlib.h> | 
 | #endif | 
 |  | 
 | #ifdef USE_GSKIT | 
 | #include <gskssl.h> | 
 | #include <qsoasync.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 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. */ | 
 |  | 
 | struct buffer_t { | 
 |   unsigned long size;            /* Buffer size. */ | 
 |   char *buf;                     /* Buffer address. */ | 
 | }; | 
 |  | 
 |  | 
 | 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 struct buffer_t *locbufs; | 
 |  | 
 | char *(*Curl_thread_buffer)(localkey_t key, long size) = buffer_undef; | 
 |  | 
 | static void thdbufdestroy(void *private) | 
 | { | 
 |   if(private) { | 
 |     struct buffer_t *p = (struct buffer_t *) private; | 
 |     localkey_t i; | 
 |  | 
 |     for(i = (localkey_t) 0; i < LK_LAST; i++) { | 
 |       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 = (struct buffer_t *) NULL; | 
 |   } | 
 |  | 
 |   Curl_thread_buffer = buffer_undef; | 
 | } | 
 |  | 
 |  | 
 | static char * | 
 | get_buffer(struct 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) { | 
 |     buf->buf = malloc(size); | 
 |     if(buf->buf) | 
 |       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. */ | 
 |  | 
 |   cp = realloc(buf->buf, size); | 
 |   if(cp) { | 
 |     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) | 
 | { | 
 |   struct 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 = (struct buffer_t *) pthread_getspecific(thdkey); | 
 |  | 
 |   if(!bufs) { | 
 |     if(size < 0) | 
 |       return (char *) NULL;             /* No buffer yet. */ | 
 |  | 
 |     /* Allocate buffer descriptors for the current thread. */ | 
 |  | 
 |     bufs = calloc((size_t) LK_LAST, sizeof(*bufs)); | 
 |     if(!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 { | 
 |       locbufs = calloc((size_t) LK_LAST, sizeof(*locbufs)); | 
 |       if(!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); | 
 | } | 
 |  | 
 |  | 
 | static char * | 
 | set_thread_string(localkey_t key, const char *s) | 
 | { | 
 |   int i; | 
 |   char *cp; | 
 |  | 
 |   if(!s) | 
 |     return (char *) NULL; | 
 |  | 
 |   i = strlen(s) + 1; | 
 |   cp = Curl_thread_buffer(key, MAX_CONV_EXPANSION * i + 1); | 
 |  | 
 |   if(cp) { | 
 |     i = QadrtConvertE2A(cp, s, MAX_CONV_EXPANSION * i, i); | 
 |     cp[i] = '\0'; | 
 |   } | 
 |  | 
 |   return cp; | 
 | } | 
 |  | 
 |  | 
 | 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 = NULL; | 
 |   char *eservname = NULL; | 
 |   int status; | 
 |  | 
 |   if(nodename && nodenamelen) { | 
 |     enodename = malloc(nodenamelen); | 
 |     if(!enodename) | 
 |       return EAI_MEMORY; | 
 |   } | 
 |  | 
 |   if(servname && servnamelen) { | 
 |     eservname = malloc(servnamelen); | 
 |     if(!eservname) { | 
 |       free(enodename); | 
 |       return EAI_MEMORY; | 
 |     } | 
 |   } | 
 |  | 
 |   status = getnameinfo(sa, salen, enodename, nodenamelen, | 
 |                        eservname, servnamelen, flags); | 
 |  | 
 |   if(!status) { | 
 |     int i; | 
 |     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'; | 
 |     } | 
 |   } | 
 |  | 
 |   free(enodename); | 
 |   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); | 
 |  | 
 |     enodename = malloc(i + 1); | 
 |     if(!enodename) | 
 |       return EAI_MEMORY; | 
 |  | 
 |     i = QadrtConvertA2E(enodename, nodename, i, i); | 
 |     enodename[i] = '\0'; | 
 |   } | 
 |  | 
 |   if(servname) { | 
 |     i = strlen(servname); | 
 |  | 
 |     eservname = malloc(i + 1); | 
 |     if(!eservname) { | 
 |       free(enodename); | 
 |       return EAI_MEMORY; | 
 |     } | 
 |  | 
 |     QadrtConvertA2E(eservname, servname, i, i); | 
 |     eservname[i] = '\0'; | 
 |   } | 
 |  | 
 |   status = getaddrinfo(enodename, eservname, hints, res); | 
 |   free(enodename); | 
 |   free(eservname); | 
 |   return status; | 
 | } | 
 |  | 
 | #ifdef USE_GSKIT | 
 |  | 
 | /* ASCII wrappers for the GSKit procedures. */ | 
 |  | 
 | /* | 
 |  * EBCDIC --> ASCII string mapping table. | 
 |  * Some strings returned by GSKit are dynamically allocated and automatically | 
 |  * released when closing the handle. | 
 |  * To provide the same functionality, we use a "private" handle that | 
 |  * holds the GSKit handle and a list of string mappings. This will allow | 
 |  * avoid conversion of already converted strings and releasing them upon | 
 |  * close time. | 
 |  */ | 
 |  | 
 | struct gskstrlist { | 
 |   struct gskstrlist *next; | 
 |   const char *ebcdicstr; | 
 |   const char *asciistr; | 
 | }; | 
 |  | 
 | struct Curl_gsk_descriptor { | 
 |   gsk_handle h; | 
 |   struct gskstrlist *strlist; | 
 | }; | 
 |  | 
 | int Curl_gsk_environment_open(gsk_handle *my_env_handle) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |   int rc; | 
 |  | 
 |   if(!my_env_handle) | 
 |     return GSK_OS400_ERROR_INVALID_POINTER; | 
 |   p = (struct Curl_gsk_descriptor *) malloc(sizeof(*p)); | 
 |   if(!p) | 
 |     return GSK_INSUFFICIENT_STORAGE; | 
 |   p->strlist = (struct gskstrlist *) NULL; | 
 |   rc = gsk_environment_open(&p->h); | 
 |   if(rc != GSK_OK) | 
 |     free(p); | 
 |   else | 
 |     *my_env_handle = (gsk_handle) p; | 
 |   return rc; | 
 | } | 
 |  | 
 | int Curl_gsk_secure_soc_open(gsk_handle my_env_handle, | 
 |                              gsk_handle *my_session_handle) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |   gsk_handle h; | 
 |   int rc; | 
 |  | 
 |   if(!my_env_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   if(!my_session_handle) | 
 |     return GSK_OS400_ERROR_INVALID_POINTER; | 
 |   h = ((struct Curl_gsk_descriptor *) my_env_handle)->h; | 
 |   p = (struct Curl_gsk_descriptor *) malloc(sizeof(*p)); | 
 |   if(!p) | 
 |     return GSK_INSUFFICIENT_STORAGE; | 
 |   p->strlist = (struct gskstrlist *) NULL; | 
 |   rc = gsk_secure_soc_open(h, &p->h); | 
 |   if(rc != GSK_OK) | 
 |     free(p); | 
 |   else | 
 |     *my_session_handle = (gsk_handle) p; | 
 |   return rc; | 
 | } | 
 |  | 
 | static void gsk_free_handle(struct Curl_gsk_descriptor *p) | 
 | { | 
 |   struct gskstrlist *q; | 
 |  | 
 |   while((q = p->strlist)) { | 
 |     p->strlist = q; | 
 |     free((void *) q->asciistr); | 
 |     free(q); | 
 |   } | 
 |   free(p); | 
 | } | 
 |  | 
 | int Curl_gsk_environment_close(gsk_handle *my_env_handle) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |   int rc; | 
 |  | 
 |   if(!my_env_handle) | 
 |     return GSK_OS400_ERROR_INVALID_POINTER; | 
 |   if(!*my_env_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) *my_env_handle; | 
 |   rc = gsk_environment_close(&p->h); | 
 |   if(rc == GSK_OK) { | 
 |     gsk_free_handle(p); | 
 |     *my_env_handle = (gsk_handle) NULL; | 
 |   } | 
 |   return rc; | 
 | } | 
 |  | 
 |  | 
 | int Curl_gsk_secure_soc_close(gsk_handle *my_session_handle) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |   int rc; | 
 |  | 
 |   if(!my_session_handle) | 
 |     return GSK_OS400_ERROR_INVALID_POINTER; | 
 |   if(!*my_session_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) *my_session_handle; | 
 |   rc = gsk_secure_soc_close(&p->h); | 
 |   if(rc == GSK_OK) { | 
 |     gsk_free_handle(p); | 
 |     *my_session_handle = (gsk_handle) NULL; | 
 |   } | 
 |   return rc; | 
 | } | 
 |  | 
 | int Curl_gsk_environment_init(gsk_handle my_env_handle) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_env_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_env_handle; | 
 |   return gsk_environment_init(p->h); | 
 | } | 
 |  | 
 |  | 
 | int Curl_gsk_secure_soc_init(gsk_handle my_session_handle) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_session_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_session_handle; | 
 |   return gsk_secure_soc_init(p->h); | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle, GSK_BUF_ID bufID, | 
 |                                 const char *buffer, int bufSize) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |   char *ebcdicbuf; | 
 |   int rc; | 
 |  | 
 |   if(!my_gsk_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   if(!buffer) | 
 |     return GSK_OS400_ERROR_INVALID_POINTER; | 
 |   if(bufSize < 0) | 
 |     return GSK_ATTRIBUTE_INVALID_LENGTH; | 
 |   p = (struct Curl_gsk_descriptor *) my_gsk_handle; | 
 |   if(!bufSize) | 
 |     bufSize = strlen(buffer); | 
 |   ebcdicbuf = malloc(bufSize + 1); | 
 |   if(!ebcdicbuf) | 
 |     return GSK_INSUFFICIENT_STORAGE; | 
 |   QadrtConvertA2E(ebcdicbuf, buffer, bufSize, bufSize); | 
 |   ebcdicbuf[bufSize] = '\0'; | 
 |   rc = gsk_attribute_set_buffer(p->h, bufID, ebcdicbuf, bufSize); | 
 |   free(ebcdicbuf); | 
 |   return rc; | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle, GSK_ENUM_ID enumID, | 
 |                             GSK_ENUM_VALUE enumValue) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_gsk_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_gsk_handle; | 
 |   return gsk_attribute_set_enum(p->h, enumID, enumValue); | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle, | 
 |                                      GSK_NUM_ID numID, int numValue) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_gsk_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_gsk_handle; | 
 |   return gsk_attribute_set_numeric_value(p->h, numID, numValue); | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle, | 
 |                                 GSK_CALLBACK_ID callBackID, | 
 |                                 void *callBackAreaPtr) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_gsk_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_gsk_handle; | 
 |   return gsk_attribute_set_callback(p->h, callBackID, callBackAreaPtr); | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | cachestring(struct Curl_gsk_descriptor *p, | 
 |             const char *ebcdicbuf, int bufsize, const char **buffer) | 
 | { | 
 |   int rc; | 
 |   char *asciibuf; | 
 |   struct gskstrlist *sp; | 
 |  | 
 |   for(sp = p->strlist; sp; sp = sp->next) | 
 |     if(sp->ebcdicstr == ebcdicbuf) | 
 |       break; | 
 |   if(!sp) { | 
 |     sp = (struct gskstrlist *) malloc(sizeof(*sp)); | 
 |     if(!sp) | 
 |       return GSK_INSUFFICIENT_STORAGE; | 
 |     asciibuf = malloc(bufsize + 1); | 
 |     if(!asciibuf) { | 
 |       free(sp); | 
 |       return GSK_INSUFFICIENT_STORAGE; | 
 |     } | 
 |     QadrtConvertE2A(asciibuf, ebcdicbuf, bufsize, bufsize); | 
 |     asciibuf[bufsize] = '\0'; | 
 |     sp->ebcdicstr = ebcdicbuf; | 
 |     sp->asciistr = asciibuf; | 
 |     sp->next = p->strlist; | 
 |     p->strlist = sp; | 
 |   } | 
 |   *buffer = sp->asciistr; | 
 |   return GSK_OK; | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle, GSK_BUF_ID bufID, | 
 |                                 const char **buffer, int *bufSize) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |   int rc; | 
 |   const char *mybuf; | 
 |   int mylen; | 
 |  | 
 |   if(!my_gsk_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   if(!buffer || !bufSize) | 
 |     return GSK_OS400_ERROR_INVALID_POINTER; | 
 |   p = (struct Curl_gsk_descriptor *) my_gsk_handle; | 
 |   rc = gsk_attribute_get_buffer(p->h, bufID, &mybuf, &mylen); | 
 |   if(rc != GSK_OK) | 
 |     return rc; | 
 |   rc = cachestring(p, mybuf, mylen, buffer); | 
 |   if(rc == GSK_OK) | 
 |     *bufSize = mylen; | 
 |   return rc; | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle, GSK_ENUM_ID enumID, | 
 |                             GSK_ENUM_VALUE *enumValue) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_gsk_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_gsk_handle; | 
 |   return gsk_attribute_get_enum(p->h, enumID, enumValue); | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle, | 
 |                                      GSK_NUM_ID numID, int *numValue) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_gsk_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_gsk_handle; | 
 |   return gsk_attribute_get_numeric_value(p->h, numID, numValue); | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle, | 
 |                                  GSK_CERT_ID certID, | 
 |                                  const gsk_cert_data_elem **certDataElem, | 
 |                                  int *certDataElementCount) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_gsk_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_gsk_handle; | 
 |   /* No need to convert code: text results are already in ASCII. */ | 
 |   return gsk_attribute_get_cert_info(p->h, certID, | 
 |                                      certDataElem, certDataElementCount); | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_secure_soc_misc(gsk_handle my_session_handle, GSK_MISC_ID miscID) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_session_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_session_handle; | 
 |   return gsk_secure_soc_misc(p->h, miscID); | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_secure_soc_read(gsk_handle my_session_handle, char *readBuffer, | 
 |                          int readBufSize, int *amtRead) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_session_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_session_handle; | 
 |   return gsk_secure_soc_read(p->h, readBuffer, readBufSize, amtRead); | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_gsk_secure_soc_write(gsk_handle my_session_handle, char *writeBuffer, | 
 |                           int writeBufSize, int *amtWritten) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_session_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_session_handle; | 
 |   return gsk_secure_soc_write(p->h, writeBuffer, writeBufSize, amtWritten); | 
 | } | 
 |  | 
 |  | 
 | const char * | 
 | Curl_gsk_strerror_a(int gsk_return_value) | 
 | { | 
 |   return set_thread_string(LK_GSK_ERROR, gsk_strerror(gsk_return_value)); | 
 | } | 
 |  | 
 | int | 
 | Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle, | 
 |                               int IOCompletionPort, | 
 |                               Qso_OverlappedIO_t *communicationsArea) | 
 | { | 
 |   struct Curl_gsk_descriptor *p; | 
 |  | 
 |   if(!my_session_handle) | 
 |     return GSK_INVALID_HANDLE; | 
 |   p = (struct Curl_gsk_descriptor *) my_session_handle; | 
 |   return gsk_secure_soc_startInit(p->h, IOCompletionPort, communicationsArea); | 
 | } | 
 |  | 
 | #endif /* USE_GSKIT */ | 
 |  | 
 | #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 = buf->length; | 
 |  | 
 |   /* Convert `buf' in place, from EBCDIC to ASCII. | 
 |      If error, release the buffer and return -1. Else return 0. */ | 
 |  | 
 |   if(i) { | 
 |     char *t = malloc(i); | 
 |     if(!t) { | 
 |       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; | 
 |  | 
 |   in.value = malloc(i + 1); | 
 |   if(!in.value) { | 
 |     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; | 
 |   gss_buffer_desc in; | 
 |   gss_buffer_t inp; | 
 |  | 
 |   in.value = NULL; | 
 |   inp = input_token; | 
 |  | 
 |   if(inp) { | 
 |     if(inp->length && inp->value) { | 
 |       unsigned int i = inp->length; | 
 |  | 
 |       in.value = malloc(i + 1); | 
 |       if(!in.value) { | 
 |         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); | 
 |   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); | 
 |  | 
 |   ehost = malloc(i + 1); | 
 |   if(!ehost) | 
 |     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); | 
 |  | 
 |     edn = malloc(i + 1); | 
 |     if(!edn) | 
 |       return LDAP_NO_MEMORY; | 
 |  | 
 |     QadrtConvertA2E(edn, dn, i, i); | 
 |     edn[i] = '\0'; | 
 |   } | 
 |  | 
 |   if(passwd) { | 
 |     i = strlen(passwd); | 
 |  | 
 |     epasswd = malloc(i + 1); | 
 |     if(!epasswd) { | 
 |       free(edn); | 
 |       return LDAP_NO_MEMORY; | 
 |     } | 
 |  | 
 |     QadrtConvertA2E(epasswd, passwd, i, i); | 
 |     epasswd[i] = '\0'; | 
 |   } | 
 |  | 
 |   i = ldap_simple_bind_s(ld, edn, epasswd); | 
 |   free(epasswd); | 
 |   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); | 
 |  | 
 |     ebase = malloc(i + 1); | 
 |     if(!ebase) | 
 |       status = LDAP_NO_MEMORY; | 
 |     else { | 
 |       QadrtConvertA2E(ebase, base, i, i); | 
 |       ebase[i] = '\0'; | 
 |     } | 
 |   } | 
 |  | 
 |   if(filter && status == LDAP_SUCCESS) { | 
 |     i = strlen(filter); | 
 |  | 
 |     efilter = malloc(i + 1); | 
 |     if(!efilter) | 
 |       status = LDAP_NO_MEMORY; | 
 |     else { | 
 |       QadrtConvertA2E(efilter, filter, i, i); | 
 |       efilter[i] = '\0'; | 
 |     } | 
 |   } | 
 |  | 
 |   if(attrs && status == LDAP_SUCCESS) { | 
 |     for(i = 0; attrs[i++];) | 
 |       ; | 
 |  | 
 |     eattrs = calloc(i, sizeof(*eattrs)); | 
 |     if(!eattrs) | 
 |       status = LDAP_NO_MEMORY; | 
 |     else { | 
 |       for(j = 0; attrs[j]; j++) { | 
 |         i = strlen(attrs[j]); | 
 |  | 
 |         eattrs[j] = malloc(i + 1); | 
 |         if(!eattrs[j]) { | 
 |           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); | 
 |   } | 
 |  | 
 |   free(efilter); | 
 |   free(ebase); | 
 |   return status; | 
 | } | 
 |  | 
 |  | 
 | struct berval ** | 
 | Curl_ldap_get_values_len_a(void *ld, LDAPMessage *entry, const char *attr) | 
 | { | 
 |   char *cp; | 
 |   struct berval **result; | 
 |  | 
 |   cp = (char *) NULL; | 
 |  | 
 |   if(attr) { | 
 |     int i = strlen(attr); | 
 |  | 
 |     cp = malloc(i + 1); | 
 |     if(!cp) { | 
 |       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); | 
 |   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) | 
 | { | 
 |   return set_thread_string(LK_LDAP_ERROR, ldap_err2string(error)); | 
 | } | 
 |  | 
 | 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); | 
 |  | 
 |   cp2 = malloc(i + 1); | 
 |   if(!cp2) | 
 |     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); | 
 |  | 
 |   cp2 = malloc(i + 1); | 
 |   if(!cp2) | 
 |     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); | 
 |  | 
 |   cp2 = malloc(i + 1); | 
 |   if(!cp2) | 
 |     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 | 
 | sockaddr2ebcdic(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 to 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'; | 
 |     srclen = i + offsetof(struct sockaddr_un, sun_path); | 
 |   } | 
 |  | 
 |   return srclen; | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | sockaddr2ascii(struct sockaddr *dstaddr, int dstlen, | 
 |                const struct sockaddr_storage *srcaddr, int srclen) | 
 | { | 
 |   const struct sockaddr_un *srcu; | 
 |   struct sockaddr_un *dstu; | 
 |   unsigned int dstsize; | 
 |  | 
 |   /* Convert a socket address to ASCII, if needed. */ | 
 |  | 
 |   if(!srclen) | 
 |     return 0; | 
 |   if(srclen > dstlen) | 
 |     srclen = dstlen; | 
 |   if(!srcaddr || srclen < 0) { | 
 |     errno = EINVAL; | 
 |     return -1; | 
 |   } | 
 |  | 
 |   memcpy((char *) dstaddr, (char *) srcaddr, srclen); | 
 |  | 
 |   if(srclen >= offsetof(struct sockaddr_storage, ss_family) + | 
 |      sizeof(srcaddr->ss_family)) { | 
 |     switch(srcaddr->ss_family) { | 
 |  | 
 |     case AF_UNIX: | 
 |       srcu = (const struct sockaddr_un *) srcaddr; | 
 |       dstu = (struct sockaddr_un *) dstaddr; | 
 |       dstsize = dstlen - offsetof(struct sockaddr_un, sun_path); | 
 |       srclen -= offsetof(struct sockaddr_un, sun_path); | 
 |       if(dstsize > 0 && srclen > 0) { | 
 |         srclen = QadrtConvertE2A(dstu->sun_path, srcu->sun_path, | 
 |                                  dstsize - 1, srclen); | 
 |         dstu->sun_path[srclen] = '\0'; | 
 |       } | 
 |       srclen += offsetof(struct sockaddr_un, sun_path); | 
 |     } | 
 |   } | 
 |  | 
 |   return srclen; | 
 | } | 
 |  | 
 | int | 
 | Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen) | 
 | { | 
 |   int i; | 
 |   struct sockaddr_storage laddr; | 
 |  | 
 |   i = sockaddr2ebcdic(&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 = sockaddr2ebcdic(&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 = sockaddr2ebcdic(&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 rcvlen; | 
 |   struct sockaddr_storage laddr; | 
 |   int laddrlen = sizeof(laddr); | 
 |  | 
 |   if(!fromaddr || !addrlen || *addrlen <= 0) | 
 |     return recvfrom(sd, buffer, buflen, flags, fromaddr, addrlen); | 
 |  | 
 |   laddr.ss_family = AF_UNSPEC;          /* To detect if unused. */ | 
 |   rcvlen = recvfrom(sd, buffer, buflen, flags, | 
 |                     (struct sockaddr *) &laddr, &laddrlen); | 
 |  | 
 |   if(rcvlen < 0) | 
 |     return rcvlen; | 
 |  | 
 |   if(laddr.ss_family == AF_UNSPEC) | 
 |     laddrlen = 0; | 
 |   else { | 
 |     laddrlen = sockaddr2ascii(fromaddr, *addrlen, &laddr, laddrlen); | 
 |     if(laddrlen < 0) | 
 |       return laddrlen; | 
 |   } | 
 |   *addrlen = laddrlen; | 
 |   return rcvlen; | 
 | } | 
 |  | 
 | int | 
 | Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen) | 
 | { | 
 |   struct sockaddr_storage laddr; | 
 |   int laddrlen = sizeof(laddr); | 
 |   int retcode = getpeername(sd, (struct sockaddr *) &laddr, &laddrlen); | 
 |  | 
 |   if(!retcode) { | 
 |     laddrlen = sockaddr2ascii(addr, *addrlen, &laddr, laddrlen); | 
 |     if(laddrlen < 0) | 
 |       return laddrlen; | 
 |     *addrlen = laddrlen; | 
 |   } | 
 |  | 
 |   return retcode; | 
 | } | 
 |  | 
 | int | 
 | Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen) | 
 | { | 
 |   struct sockaddr_storage laddr; | 
 |   int laddrlen = sizeof(laddr); | 
 |   int retcode = getsockname(sd, (struct sockaddr *) &laddr, &laddrlen); | 
 |  | 
 |   if(!retcode) { | 
 |     laddrlen = sockaddr2ascii(addr, *addrlen, &laddr, laddrlen); | 
 |     if(laddrlen < 0) | 
 |       return laddrlen; | 
 |     *addrlen = laddrlen; | 
 |   } | 
 |  | 
 |   return retcode; | 
 | } | 
 |  | 
 |  | 
 | #ifdef HAVE_LIBZ | 
 | const char * | 
 | Curl_os400_zlibVersion(void) | 
 | { | 
 |   return set_thread_string(LK_ZLIB_VERSION, zlibVersion()); | 
 | } | 
 |  | 
 |  | 
 | int | 
 | Curl_os400_inflateInit_(z_streamp strm, const char *version, int stream_size) | 
 | { | 
 |   z_const char *msgb4 = strm->msg; | 
 |   int ret; | 
 |  | 
 |   ret = inflateInit(strm); | 
 |  | 
 |   if(strm->msg != msgb4) | 
 |     strm->msg = set_thread_string(LK_ZLIB_MSG, strm->msg); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | int | 
 | Curl_os400_inflateInit2_(z_streamp strm, int windowBits, | 
 |                          const char *version, int stream_size) | 
 | { | 
 |   z_const char *msgb4 = strm->msg; | 
 |   int ret; | 
 |  | 
 |   ret = inflateInit2(strm, windowBits); | 
 |  | 
 |   if(strm->msg != msgb4) | 
 |     strm->msg = set_thread_string(LK_ZLIB_MSG, strm->msg); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | int | 
 | Curl_os400_inflate(z_streamp strm, int flush) | 
 | { | 
 |   z_const char *msgb4 = strm->msg; | 
 |   int ret; | 
 |  | 
 |   ret = inflate(strm, flush); | 
 |  | 
 |   if(strm->msg != msgb4) | 
 |     strm->msg = set_thread_string(LK_ZLIB_MSG, strm->msg); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | int | 
 | Curl_os400_inflateEnd(z_streamp strm) | 
 | { | 
 |   z_const char *msgb4 = strm->msg; | 
 |   int ret; | 
 |  | 
 |   ret = inflateEnd(strm); | 
 |  | 
 |   if(strm->msg != msgb4) | 
 |     strm->msg = set_thread_string(LK_ZLIB_MSG, strm->msg); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | #endif |