1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 *
24 ***************************************************************************/
25
26 /* OS/400 additional support. */
27
28 #include <curl/curl.h>
29 #include "config-os400.h" /* Not curl_setup.h: we only need some defines. */
30
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34
35 #include <stdlib.h>
36 #include <stddef.h>
37 #include <string.h>
38 #include <pthread.h>
39 #include <netdb.h>
40 #include <qadrt.h>
41 #include <errno.h>
42
43 #ifdef HAVE_LIBZ
44 #include <zlib.h>
45 #endif
46
47 #ifdef USE_GSKIT
48 #include <gskssl.h>
49 #include <qsoasync.h>
50 #endif
51
52 #ifdef HAVE_GSSAPI
53 #include <gssapi.h>
54 #endif
55
56 #ifndef CURL_DISABLE_LDAP
57 #include <ldap.h>
58 #endif
59
60 #include <netinet/in.h>
61 #include <arpa/inet.h>
62
63 #include "os400sys.h"
64
65 /**
66 *** QADRT OS/400 ASCII runtime defines only the most used procedures, but a
67 *** lot of them are not supported. This module implements ASCII wrappers for
68 *** those that are used by libcurl, but not defined by QADRT.
69 **/
70
71 #pragma convert(0) /* Restore EBCDIC. */
72
73 #define MIN_BYTE_GAIN 1024 /* Minimum gain when shortening a buffer. */
74
75 struct buffer_t {
76 unsigned long size; /* Buffer size. */
77 char *buf; /* Buffer address. */
78 };
79
80
81 static char *buffer_undef(localkey_t key, long size);
82 static char *buffer_threaded(localkey_t key, long size);
83 static char *buffer_unthreaded(localkey_t key, long size);
84
85 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
86 static pthread_key_t thdkey;
87 static struct buffer_t *locbufs;
88
89 char *(*Curl_thread_buffer)(localkey_t key, long size) = buffer_undef;
90
thdbufdestroy(void * private)91 static void thdbufdestroy(void *private)
92 {
93 if(private) {
94 struct buffer_t *p = (struct buffer_t *) private;
95 localkey_t i;
96
97 for(i = (localkey_t) 0; i < LK_LAST; i++) {
98 free(p->buf);
99 p++;
100 }
101
102 free(private);
103 }
104 }
105
106
107 static void
terminate(void)108 terminate(void)
109 {
110 if(Curl_thread_buffer == buffer_threaded) {
111 locbufs = pthread_getspecific(thdkey);
112 pthread_setspecific(thdkey, (void *) NULL);
113 pthread_key_delete(thdkey);
114 }
115
116 if(Curl_thread_buffer != buffer_undef) {
117 thdbufdestroy((void *) locbufs);
118 locbufs = (struct buffer_t *) NULL;
119 }
120
121 Curl_thread_buffer = buffer_undef;
122 }
123
124
125 static char *
get_buffer(struct buffer_t * buf,long size)126 get_buffer(struct buffer_t *buf, long size)
127 {
128 char *cp;
129
130 /* If `size' >= 0, make sure buffer at `buf' is at least `size'-byte long.
131 Return the buffer address. */
132
133 if(size < 0)
134 return buf->buf;
135
136 if(!buf->buf) {
137 buf->buf = malloc(size);
138 if(buf->buf)
139 buf->size = size;
140
141 return buf->buf;
142 }
143
144 if((unsigned long) size <= buf->size) {
145 /* Shorten the buffer only if it frees a significant byte count. This
146 avoids some realloc() overhead. */
147
148 if(buf->size - size < MIN_BYTE_GAIN)
149 return buf->buf;
150 }
151
152 /* Resize the buffer. */
153
154 cp = realloc(buf->buf, size);
155 if(cp) {
156 buf->buf = cp;
157 buf->size = size;
158 }
159 else if(size <= buf->size)
160 cp = buf->buf;
161
162 return cp;
163 }
164
165
166 static char *
buffer_unthreaded(localkey_t key,long size)167 buffer_unthreaded(localkey_t key, long size)
168 {
169 return get_buffer(locbufs + key, size);
170 }
171
172
173 static char *
buffer_threaded(localkey_t key,long size)174 buffer_threaded(localkey_t key, long size)
175 {
176 struct buffer_t *bufs;
177
178 /* Get the buffer for the given local key in the current thread, and
179 make sure it is at least `size'-byte long. Set `size' to < 0 to get
180 its address only. */
181
182 bufs = (struct buffer_t *) pthread_getspecific(thdkey);
183
184 if(!bufs) {
185 if(size < 0)
186 return (char *) NULL; /* No buffer yet. */
187
188 /* Allocate buffer descriptors for the current thread. */
189
190 bufs = calloc((size_t) LK_LAST, sizeof(*bufs));
191 if(!bufs)
192 return (char *) NULL;
193
194 if(pthread_setspecific(thdkey, (void *) bufs)) {
195 free(bufs);
196 return (char *) NULL;
197 }
198 }
199
200 return get_buffer(bufs + key, size);
201 }
202
203
204 static char *
buffer_undef(localkey_t key,long size)205 buffer_undef(localkey_t key, long size)
206 {
207 /* Define the buffer system, get the buffer for the given local key in
208 the current thread, and make sure it is at least `size'-byte long.
209 Set `size' to < 0 to get its address only. */
210
211 pthread_mutex_lock(&mutex);
212
213 /* Determine if we can use pthread-specific data. */
214
215 if(Curl_thread_buffer == buffer_undef) { /* If unchanged during lock. */
216 if(!pthread_key_create(&thdkey, thdbufdestroy))
217 Curl_thread_buffer = buffer_threaded;
218 else {
219 locbufs = calloc((size_t) LK_LAST, sizeof(*locbufs));
220 if(!locbufs) {
221 pthread_mutex_unlock(&mutex);
222 return (char *) NULL;
223 }
224 else
225 Curl_thread_buffer = buffer_unthreaded;
226 }
227
228 atexit(terminate);
229 }
230
231 pthread_mutex_unlock(&mutex);
232 return Curl_thread_buffer(key, size);
233 }
234
235
236 static char *
set_thread_string(localkey_t key,const char * s)237 set_thread_string(localkey_t key, const char *s)
238 {
239 int i;
240 char *cp;
241
242 if(!s)
243 return (char *) NULL;
244
245 i = strlen(s) + 1;
246 cp = Curl_thread_buffer(key, MAX_CONV_EXPANSION * i + 1);
247
248 if(cp) {
249 i = QadrtConvertE2A(cp, s, MAX_CONV_EXPANSION * i, i);
250 cp[i] = '\0';
251 }
252
253 return cp;
254 }
255
256
257 int
Curl_getnameinfo_a(const struct sockaddr * sa,socklen_t salen,char * nodename,socklen_t nodenamelen,char * servname,socklen_t servnamelen,int flags)258 Curl_getnameinfo_a(const struct sockaddr *sa, socklen_t salen,
259 char *nodename, socklen_t nodenamelen,
260 char *servname, socklen_t servnamelen,
261 int flags)
262 {
263 char *enodename = NULL;
264 char *eservname = NULL;
265 int status;
266
267 if(nodename && nodenamelen) {
268 enodename = malloc(nodenamelen);
269 if(!enodename)
270 return EAI_MEMORY;
271 }
272
273 if(servname && servnamelen) {
274 eservname = malloc(servnamelen);
275 if(!eservname) {
276 free(enodename);
277 return EAI_MEMORY;
278 }
279 }
280
281 status = getnameinfo(sa, salen, enodename, nodenamelen,
282 eservname, servnamelen, flags);
283
284 if(!status) {
285 int i;
286 if(enodename) {
287 i = QadrtConvertE2A(nodename, enodename,
288 nodenamelen - 1, strlen(enodename));
289 nodename[i] = '\0';
290 }
291
292 if(eservname) {
293 i = QadrtConvertE2A(servname, eservname,
294 servnamelen - 1, strlen(eservname));
295 servname[i] = '\0';
296 }
297 }
298
299 free(enodename);
300 free(eservname);
301 return status;
302 }
303
304 int
Curl_getaddrinfo_a(const char * nodename,const char * servname,const struct addrinfo * hints,struct addrinfo ** res)305 Curl_getaddrinfo_a(const char *nodename, const char *servname,
306 const struct addrinfo *hints,
307 struct addrinfo **res)
308 {
309 char *enodename;
310 char *eservname;
311 int status;
312 int i;
313
314 enodename = (char *) NULL;
315 eservname = (char *) NULL;
316
317 if(nodename) {
318 i = strlen(nodename);
319
320 enodename = malloc(i + 1);
321 if(!enodename)
322 return EAI_MEMORY;
323
324 i = QadrtConvertA2E(enodename, nodename, i, i);
325 enodename[i] = '\0';
326 }
327
328 if(servname) {
329 i = strlen(servname);
330
331 eservname = malloc(i + 1);
332 if(!eservname) {
333 free(enodename);
334 return EAI_MEMORY;
335 }
336
337 QadrtConvertA2E(eservname, servname, i, i);
338 eservname[i] = '\0';
339 }
340
341 status = getaddrinfo(enodename, eservname, hints, res);
342 free(enodename);
343 free(eservname);
344 return status;
345 }
346
347 #ifdef USE_GSKIT
348
349 /* ASCII wrappers for the GSKit procedures. */
350
351 /*
352 * EBCDIC --> ASCII string mapping table.
353 * Some strings returned by GSKit are dynamically allocated and automatically
354 * released when closing the handle.
355 * To provide the same functionality, we use a "private" handle that
356 * holds the GSKit handle and a list of string mappings. This will allow
357 * avoid conversion of already converted strings and releasing them upon
358 * close time.
359 */
360
361 struct gskstrlist {
362 struct gskstrlist *next;
363 const char *ebcdicstr;
364 const char *asciistr;
365 };
366
367 struct Curl_gsk_descriptor {
368 gsk_handle h;
369 struct gskstrlist *strlist;
370 };
371
Curl_gsk_environment_open(gsk_handle * my_env_handle)372 int Curl_gsk_environment_open(gsk_handle *my_env_handle)
373 {
374 struct Curl_gsk_descriptor *p;
375 int rc;
376
377 if(!my_env_handle)
378 return GSK_OS400_ERROR_INVALID_POINTER;
379 p = (struct Curl_gsk_descriptor *) malloc(sizeof(*p));
380 if(!p)
381 return GSK_INSUFFICIENT_STORAGE;
382 p->strlist = (struct gskstrlist *) NULL;
383 rc = gsk_environment_open(&p->h);
384 if(rc != GSK_OK)
385 free(p);
386 else
387 *my_env_handle = (gsk_handle) p;
388 return rc;
389 }
390
Curl_gsk_secure_soc_open(gsk_handle my_env_handle,gsk_handle * my_session_handle)391 int Curl_gsk_secure_soc_open(gsk_handle my_env_handle,
392 gsk_handle *my_session_handle)
393 {
394 struct Curl_gsk_descriptor *p;
395 gsk_handle h;
396 int rc;
397
398 if(!my_env_handle)
399 return GSK_INVALID_HANDLE;
400 if(!my_session_handle)
401 return GSK_OS400_ERROR_INVALID_POINTER;
402 h = ((struct Curl_gsk_descriptor *) my_env_handle)->h;
403 p = (struct Curl_gsk_descriptor *) malloc(sizeof(*p));
404 if(!p)
405 return GSK_INSUFFICIENT_STORAGE;
406 p->strlist = (struct gskstrlist *) NULL;
407 rc = gsk_secure_soc_open(h, &p->h);
408 if(rc != GSK_OK)
409 free(p);
410 else
411 *my_session_handle = (gsk_handle) p;
412 return rc;
413 }
414
gsk_free_handle(struct Curl_gsk_descriptor * p)415 static void gsk_free_handle(struct Curl_gsk_descriptor *p)
416 {
417 struct gskstrlist *q;
418
419 while((q = p->strlist)) {
420 p->strlist = q;
421 free((void *) q->asciistr);
422 free(q);
423 }
424 free(p);
425 }
426
Curl_gsk_environment_close(gsk_handle * my_env_handle)427 int Curl_gsk_environment_close(gsk_handle *my_env_handle)
428 {
429 struct Curl_gsk_descriptor *p;
430 int rc;
431
432 if(!my_env_handle)
433 return GSK_OS400_ERROR_INVALID_POINTER;
434 if(!*my_env_handle)
435 return GSK_INVALID_HANDLE;
436 p = (struct Curl_gsk_descriptor *) *my_env_handle;
437 rc = gsk_environment_close(&p->h);
438 if(rc == GSK_OK) {
439 gsk_free_handle(p);
440 *my_env_handle = (gsk_handle) NULL;
441 }
442 return rc;
443 }
444
445
Curl_gsk_secure_soc_close(gsk_handle * my_session_handle)446 int Curl_gsk_secure_soc_close(gsk_handle *my_session_handle)
447 {
448 struct Curl_gsk_descriptor *p;
449 int rc;
450
451 if(!my_session_handle)
452 return GSK_OS400_ERROR_INVALID_POINTER;
453 if(!*my_session_handle)
454 return GSK_INVALID_HANDLE;
455 p = (struct Curl_gsk_descriptor *) *my_session_handle;
456 rc = gsk_secure_soc_close(&p->h);
457 if(rc == GSK_OK) {
458 gsk_free_handle(p);
459 *my_session_handle = (gsk_handle) NULL;
460 }
461 return rc;
462 }
463
Curl_gsk_environment_init(gsk_handle my_env_handle)464 int Curl_gsk_environment_init(gsk_handle my_env_handle)
465 {
466 struct Curl_gsk_descriptor *p;
467
468 if(!my_env_handle)
469 return GSK_INVALID_HANDLE;
470 p = (struct Curl_gsk_descriptor *) my_env_handle;
471 return gsk_environment_init(p->h);
472 }
473
474
Curl_gsk_secure_soc_init(gsk_handle my_session_handle)475 int Curl_gsk_secure_soc_init(gsk_handle my_session_handle)
476 {
477 struct Curl_gsk_descriptor *p;
478
479 if(!my_session_handle)
480 return GSK_INVALID_HANDLE;
481 p = (struct Curl_gsk_descriptor *) my_session_handle;
482 return gsk_secure_soc_init(p->h);
483 }
484
485
486 int
Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle,GSK_BUF_ID bufID,const char * buffer,int bufSize)487 Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle, GSK_BUF_ID bufID,
488 const char *buffer, int bufSize)
489 {
490 struct Curl_gsk_descriptor *p;
491 char *ebcdicbuf;
492 int rc;
493
494 if(!my_gsk_handle)
495 return GSK_INVALID_HANDLE;
496 if(!buffer)
497 return GSK_OS400_ERROR_INVALID_POINTER;
498 if(bufSize < 0)
499 return GSK_ATTRIBUTE_INVALID_LENGTH;
500 p = (struct Curl_gsk_descriptor *) my_gsk_handle;
501 if(!bufSize)
502 bufSize = strlen(buffer);
503 ebcdicbuf = malloc(bufSize + 1);
504 if(!ebcdicbuf)
505 return GSK_INSUFFICIENT_STORAGE;
506 QadrtConvertA2E(ebcdicbuf, buffer, bufSize, bufSize);
507 ebcdicbuf[bufSize] = '\0';
508 rc = gsk_attribute_set_buffer(p->h, bufID, ebcdicbuf, bufSize);
509 free(ebcdicbuf);
510 return rc;
511 }
512
513
514 int
Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle,GSK_ENUM_ID enumID,GSK_ENUM_VALUE enumValue)515 Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle, GSK_ENUM_ID enumID,
516 GSK_ENUM_VALUE enumValue)
517 {
518 struct Curl_gsk_descriptor *p;
519
520 if(!my_gsk_handle)
521 return GSK_INVALID_HANDLE;
522 p = (struct Curl_gsk_descriptor *) my_gsk_handle;
523 return gsk_attribute_set_enum(p->h, enumID, enumValue);
524 }
525
526
527 int
Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle,GSK_NUM_ID numID,int numValue)528 Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle,
529 GSK_NUM_ID numID, int numValue)
530 {
531 struct Curl_gsk_descriptor *p;
532
533 if(!my_gsk_handle)
534 return GSK_INVALID_HANDLE;
535 p = (struct Curl_gsk_descriptor *) my_gsk_handle;
536 return gsk_attribute_set_numeric_value(p->h, numID, numValue);
537 }
538
539
540 int
Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle,GSK_CALLBACK_ID callBackID,void * callBackAreaPtr)541 Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle,
542 GSK_CALLBACK_ID callBackID,
543 void *callBackAreaPtr)
544 {
545 struct Curl_gsk_descriptor *p;
546
547 if(!my_gsk_handle)
548 return GSK_INVALID_HANDLE;
549 p = (struct Curl_gsk_descriptor *) my_gsk_handle;
550 return gsk_attribute_set_callback(p->h, callBackID, callBackAreaPtr);
551 }
552
553
554 static int
cachestring(struct Curl_gsk_descriptor * p,const char * ebcdicbuf,int bufsize,const char ** buffer)555 cachestring(struct Curl_gsk_descriptor *p,
556 const char *ebcdicbuf, int bufsize, const char **buffer)
557 {
558 int rc;
559 char *asciibuf;
560 struct gskstrlist *sp;
561
562 for(sp = p->strlist; sp; sp = sp->next)
563 if(sp->ebcdicstr == ebcdicbuf)
564 break;
565 if(!sp) {
566 sp = (struct gskstrlist *) malloc(sizeof(*sp));
567 if(!sp)
568 return GSK_INSUFFICIENT_STORAGE;
569 asciibuf = malloc(bufsize + 1);
570 if(!asciibuf) {
571 free(sp);
572 return GSK_INSUFFICIENT_STORAGE;
573 }
574 QadrtConvertE2A(asciibuf, ebcdicbuf, bufsize, bufsize);
575 asciibuf[bufsize] = '\0';
576 sp->ebcdicstr = ebcdicbuf;
577 sp->asciistr = asciibuf;
578 sp->next = p->strlist;
579 p->strlist = sp;
580 }
581 *buffer = sp->asciistr;
582 return GSK_OK;
583 }
584
585
586 int
Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle,GSK_BUF_ID bufID,const char ** buffer,int * bufSize)587 Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle, GSK_BUF_ID bufID,
588 const char **buffer, int *bufSize)
589 {
590 struct Curl_gsk_descriptor *p;
591 int rc;
592 const char *mybuf;
593 int mylen;
594
595 if(!my_gsk_handle)
596 return GSK_INVALID_HANDLE;
597 if(!buffer || !bufSize)
598 return GSK_OS400_ERROR_INVALID_POINTER;
599 p = (struct Curl_gsk_descriptor *) my_gsk_handle;
600 rc = gsk_attribute_get_buffer(p->h, bufID, &mybuf, &mylen);
601 if(rc != GSK_OK)
602 return rc;
603 rc = cachestring(p, mybuf, mylen, buffer);
604 if(rc == GSK_OK)
605 *bufSize = mylen;
606 return rc;
607 }
608
609
610 int
Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle,GSK_ENUM_ID enumID,GSK_ENUM_VALUE * enumValue)611 Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle, GSK_ENUM_ID enumID,
612 GSK_ENUM_VALUE *enumValue)
613 {
614 struct Curl_gsk_descriptor *p;
615
616 if(!my_gsk_handle)
617 return GSK_INVALID_HANDLE;
618 p = (struct Curl_gsk_descriptor *) my_gsk_handle;
619 return gsk_attribute_get_enum(p->h, enumID, enumValue);
620 }
621
622
623 int
Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle,GSK_NUM_ID numID,int * numValue)624 Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle,
625 GSK_NUM_ID numID, int *numValue)
626 {
627 struct Curl_gsk_descriptor *p;
628
629 if(!my_gsk_handle)
630 return GSK_INVALID_HANDLE;
631 p = (struct Curl_gsk_descriptor *) my_gsk_handle;
632 return gsk_attribute_get_numeric_value(p->h, numID, numValue);
633 }
634
635
636 int
Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle,GSK_CERT_ID certID,const gsk_cert_data_elem ** certDataElem,int * certDataElementCount)637 Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle,
638 GSK_CERT_ID certID,
639 const gsk_cert_data_elem **certDataElem,
640 int *certDataElementCount)
641 {
642 struct Curl_gsk_descriptor *p;
643
644 if(!my_gsk_handle)
645 return GSK_INVALID_HANDLE;
646 p = (struct Curl_gsk_descriptor *) my_gsk_handle;
647 /* No need to convert code: text results are already in ASCII. */
648 return gsk_attribute_get_cert_info(p->h, certID,
649 certDataElem, certDataElementCount);
650 }
651
652
653 int
Curl_gsk_secure_soc_misc(gsk_handle my_session_handle,GSK_MISC_ID miscID)654 Curl_gsk_secure_soc_misc(gsk_handle my_session_handle, GSK_MISC_ID miscID)
655 {
656 struct Curl_gsk_descriptor *p;
657
658 if(!my_session_handle)
659 return GSK_INVALID_HANDLE;
660 p = (struct Curl_gsk_descriptor *) my_session_handle;
661 return gsk_secure_soc_misc(p->h, miscID);
662 }
663
664
665 int
Curl_gsk_secure_soc_read(gsk_handle my_session_handle,char * readBuffer,int readBufSize,int * amtRead)666 Curl_gsk_secure_soc_read(gsk_handle my_session_handle, char *readBuffer,
667 int readBufSize, int *amtRead)
668 {
669 struct Curl_gsk_descriptor *p;
670
671 if(!my_session_handle)
672 return GSK_INVALID_HANDLE;
673 p = (struct Curl_gsk_descriptor *) my_session_handle;
674 return gsk_secure_soc_read(p->h, readBuffer, readBufSize, amtRead);
675 }
676
677
678 int
Curl_gsk_secure_soc_write(gsk_handle my_session_handle,char * writeBuffer,int writeBufSize,int * amtWritten)679 Curl_gsk_secure_soc_write(gsk_handle my_session_handle, char *writeBuffer,
680 int writeBufSize, int *amtWritten)
681 {
682 struct Curl_gsk_descriptor *p;
683
684 if(!my_session_handle)
685 return GSK_INVALID_HANDLE;
686 p = (struct Curl_gsk_descriptor *) my_session_handle;
687 return gsk_secure_soc_write(p->h, writeBuffer, writeBufSize, amtWritten);
688 }
689
690
691 const char *
Curl_gsk_strerror_a(int gsk_return_value)692 Curl_gsk_strerror_a(int gsk_return_value)
693 {
694 return set_thread_string(LK_GSK_ERROR, gsk_strerror(gsk_return_value));
695 }
696
697 int
Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle,int IOCompletionPort,Qso_OverlappedIO_t * communicationsArea)698 Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle,
699 int IOCompletionPort,
700 Qso_OverlappedIO_t *communicationsArea)
701 {
702 struct Curl_gsk_descriptor *p;
703
704 if(!my_session_handle)
705 return GSK_INVALID_HANDLE;
706 p = (struct Curl_gsk_descriptor *) my_session_handle;
707 return gsk_secure_soc_startInit(p->h, IOCompletionPort, communicationsArea);
708 }
709
710 #endif /* USE_GSKIT */
711
712 #ifdef HAVE_GSSAPI
713
714 /* ASCII wrappers for the GSSAPI procedures. */
715
716 static int
Curl_gss_convert_in_place(OM_uint32 * minor_status,gss_buffer_t buf)717 Curl_gss_convert_in_place(OM_uint32 *minor_status, gss_buffer_t buf)
718 {
719 unsigned int i = buf->length;
720
721 /* Convert `buf' in place, from EBCDIC to ASCII.
722 If error, release the buffer and return -1. Else return 0. */
723
724 if(i) {
725 char *t = malloc(i);
726 if(!t) {
727 gss_release_buffer(minor_status, buf);
728
729 if(minor_status)
730 *minor_status = ENOMEM;
731
732 return -1;
733 }
734
735 QadrtConvertE2A(t, buf->value, i, i);
736 memcpy(buf->value, t, i);
737 free(t);
738 }
739
740 return 0;
741 }
742
743
744 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)745 Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name,
746 gss_OID in_name_type, gss_name_t *out_name)
747 {
748 OM_uint32 rc;
749 unsigned int i;
750 gss_buffer_desc in;
751
752 if(!in_name || !in_name->value || !in_name->length)
753 return gss_import_name(minor_status, in_name, in_name_type, out_name);
754
755 memcpy((char *) &in, (char *) in_name, sizeof(in));
756 i = in.length;
757
758 in.value = malloc(i + 1);
759 if(!in.value) {
760 if(minor_status)
761 *minor_status = ENOMEM;
762
763 return GSS_S_FAILURE;
764 }
765
766 QadrtConvertA2E(in.value, in_name->value, i, i);
767 ((char *) in.value)[i] = '\0';
768 rc = gss_import_name(minor_status, &in, in_name_type, out_name);
769 free(in.value);
770 return rc;
771 }
772
773 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)774 Curl_gss_display_status_a(OM_uint32 *minor_status, OM_uint32 status_value,
775 int status_type, gss_OID mech_type,
776 gss_msg_ctx_t *message_context,
777 gss_buffer_t status_string)
778 {
779 int rc;
780
781 rc = gss_display_status(minor_status, status_value, status_type,
782 mech_type, message_context, status_string);
783
784 if(rc != GSS_S_COMPLETE || !status_string ||
785 !status_string->length || !status_string->value)
786 return rc;
787
788 /* No way to allocate a buffer here, because it will be released by
789 gss_release_buffer(). The solution is to overwrite the EBCDIC buffer
790 with ASCII to return it. */
791
792 if(Curl_gss_convert_in_place(minor_status, status_string))
793 return GSS_S_FAILURE;
794
795 return rc;
796 }
797
798 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)799 Curl_gss_init_sec_context_a(OM_uint32 *minor_status,
800 gss_cred_id_t cred_handle,
801 gss_ctx_id_t *context_handle,
802 gss_name_t target_name, gss_OID mech_type,
803 gss_flags_t req_flags, OM_uint32 time_req,
804 gss_channel_bindings_t input_chan_bindings,
805 gss_buffer_t input_token,
806 gss_OID *actual_mech_type,
807 gss_buffer_t output_token, gss_flags_t *ret_flags,
808 OM_uint32 *time_rec)
809 {
810 int rc;
811 gss_buffer_desc in;
812 gss_buffer_t inp;
813
814 in.value = NULL;
815 inp = input_token;
816
817 if(inp) {
818 if(inp->length && inp->value) {
819 unsigned int i = inp->length;
820
821 in.value = malloc(i + 1);
822 if(!in.value) {
823 if(minor_status)
824 *minor_status = ENOMEM;
825
826 return GSS_S_FAILURE;
827 }
828
829 QadrtConvertA2E(in.value, input_token->value, i, i);
830 ((char *) in.value)[i] = '\0';
831 in.length = i;
832 inp = ∈
833 }
834 }
835
836 rc = gss_init_sec_context(minor_status, cred_handle, context_handle,
837 target_name, mech_type, req_flags, time_req,
838 input_chan_bindings, inp, actual_mech_type,
839 output_token, ret_flags, time_rec);
840 free(in.value);
841
842 if(rc != GSS_S_COMPLETE || !output_token ||
843 !output_token->length || !output_token->value)
844 return rc;
845
846 /* No way to allocate a buffer here, because it will be released by
847 gss_release_buffer(). The solution is to overwrite the EBCDIC buffer
848 with ASCII to return it. */
849
850 if(Curl_gss_convert_in_place(minor_status, output_token))
851 return GSS_S_FAILURE;
852
853 return rc;
854 }
855
856
857 OM_uint32
Curl_gss_delete_sec_context_a(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)858 Curl_gss_delete_sec_context_a(OM_uint32 *minor_status,
859 gss_ctx_id_t *context_handle,
860 gss_buffer_t output_token)
861 {
862 OM_uint32 rc;
863
864 rc = gss_delete_sec_context(minor_status, context_handle, output_token);
865
866 if(rc != GSS_S_COMPLETE || !output_token ||
867 !output_token->length || !output_token->value)
868 return rc;
869
870 /* No way to allocate a buffer here, because it will be released by
871 gss_release_buffer(). The solution is to overwrite the EBCDIC buffer
872 with ASCII to return it. */
873
874 if(Curl_gss_convert_in_place(minor_status, output_token))
875 return GSS_S_FAILURE;
876
877 return rc;
878 }
879
880 #endif /* HAVE_GSSAPI */
881
882 #ifndef CURL_DISABLE_LDAP
883
884 /* ASCII wrappers for the LDAP procedures. */
885
886 void *
Curl_ldap_init_a(char * host,int port)887 Curl_ldap_init_a(char *host, int port)
888 {
889 size_t i;
890 char *ehost;
891 void *result;
892
893 if(!host)
894 return (void *) ldap_init(host, port);
895
896 i = strlen(host);
897
898 ehost = malloc(i + 1);
899 if(!ehost)
900 return (void *) NULL;
901
902 QadrtConvertA2E(ehost, host, i, i);
903 ehost[i] = '\0';
904 result = (void *) ldap_init(ehost, port);
905 free(ehost);
906 return result;
907 }
908
909 int
Curl_ldap_simple_bind_s_a(void * ld,char * dn,char * passwd)910 Curl_ldap_simple_bind_s_a(void *ld, char *dn, char *passwd)
911 {
912 int i;
913 char *edn;
914 char *epasswd;
915
916 edn = (char *) NULL;
917 epasswd = (char *) NULL;
918
919 if(dn) {
920 i = strlen(dn);
921
922 edn = malloc(i + 1);
923 if(!edn)
924 return LDAP_NO_MEMORY;
925
926 QadrtConvertA2E(edn, dn, i, i);
927 edn[i] = '\0';
928 }
929
930 if(passwd) {
931 i = strlen(passwd);
932
933 epasswd = malloc(i + 1);
934 if(!epasswd) {
935 free(edn);
936 return LDAP_NO_MEMORY;
937 }
938
939 QadrtConvertA2E(epasswd, passwd, i, i);
940 epasswd[i] = '\0';
941 }
942
943 i = ldap_simple_bind_s(ld, edn, epasswd);
944 free(epasswd);
945 free(edn);
946 return i;
947 }
948
949 int
Curl_ldap_search_s_a(void * ld,char * base,int scope,char * filter,char ** attrs,int attrsonly,LDAPMessage ** res)950 Curl_ldap_search_s_a(void *ld, char *base, int scope, char *filter,
951 char **attrs, int attrsonly, LDAPMessage **res)
952 {
953 int i;
954 int j;
955 char *ebase;
956 char *efilter;
957 char **eattrs;
958 int status;
959
960 ebase = (char *) NULL;
961 efilter = (char *) NULL;
962 eattrs = (char **) NULL;
963 status = LDAP_SUCCESS;
964
965 if(base) {
966 i = strlen(base);
967
968 ebase = malloc(i + 1);
969 if(!ebase)
970 status = LDAP_NO_MEMORY;
971 else {
972 QadrtConvertA2E(ebase, base, i, i);
973 ebase[i] = '\0';
974 }
975 }
976
977 if(filter && status == LDAP_SUCCESS) {
978 i = strlen(filter);
979
980 efilter = malloc(i + 1);
981 if(!efilter)
982 status = LDAP_NO_MEMORY;
983 else {
984 QadrtConvertA2E(efilter, filter, i, i);
985 efilter[i] = '\0';
986 }
987 }
988
989 if(attrs && status == LDAP_SUCCESS) {
990 for(i = 0; attrs[i++];)
991 ;
992
993 eattrs = calloc(i, sizeof(*eattrs));
994 if(!eattrs)
995 status = LDAP_NO_MEMORY;
996 else {
997 for(j = 0; attrs[j]; j++) {
998 i = strlen(attrs[j]);
999
1000 eattrs[j] = malloc(i + 1);
1001 if(!eattrs[j]) {
1002 status = LDAP_NO_MEMORY;
1003 break;
1004 }
1005
1006 QadrtConvertA2E(eattrs[j], attrs[j], i, i);
1007 eattrs[j][i] = '\0';
1008 }
1009 }
1010 }
1011
1012 if(status == LDAP_SUCCESS)
1013 status = ldap_search_s(ld, ebase? ebase: "", scope,
1014 efilter? efilter: "(objectclass=*)",
1015 eattrs, attrsonly, res);
1016
1017 if(eattrs) {
1018 for(j = 0; eattrs[j]; j++)
1019 free(eattrs[j]);
1020
1021 free(eattrs);
1022 }
1023
1024 free(efilter);
1025 free(ebase);
1026 return status;
1027 }
1028
1029
1030 struct berval **
Curl_ldap_get_values_len_a(void * ld,LDAPMessage * entry,const char * attr)1031 Curl_ldap_get_values_len_a(void *ld, LDAPMessage *entry, const char *attr)
1032 {
1033 char *cp;
1034 struct berval **result;
1035
1036 cp = (char *) NULL;
1037
1038 if(attr) {
1039 int i = strlen(attr);
1040
1041 cp = malloc(i + 1);
1042 if(!cp) {
1043 ldap_set_lderrno(ld, LDAP_NO_MEMORY, NULL,
1044 ldap_err2string(LDAP_NO_MEMORY));
1045 return (struct berval **) NULL;
1046 }
1047
1048 QadrtConvertA2E(cp, attr, i, i);
1049 cp[i] = '\0';
1050 }
1051
1052 result = ldap_get_values_len(ld, entry, cp);
1053 free(cp);
1054
1055 /* Result data are binary in nature, so they haven't been
1056 converted to EBCDIC. Therefore do not convert. */
1057
1058 return result;
1059 }
1060
1061 char *
Curl_ldap_err2string_a(int error)1062 Curl_ldap_err2string_a(int error)
1063 {
1064 return set_thread_string(LK_LDAP_ERROR, ldap_err2string(error));
1065 }
1066
1067 char *
Curl_ldap_get_dn_a(void * ld,LDAPMessage * entry)1068 Curl_ldap_get_dn_a(void *ld, LDAPMessage *entry)
1069 {
1070 int i;
1071 char *cp;
1072 char *cp2;
1073
1074 cp = ldap_get_dn(ld, entry);
1075
1076 if(!cp)
1077 return cp;
1078
1079 i = strlen(cp);
1080
1081 cp2 = malloc(i + 1);
1082 if(!cp2)
1083 return cp2;
1084
1085 QadrtConvertE2A(cp2, cp, i, i);
1086 cp2[i] = '\0';
1087
1088 /* No way to allocate a buffer here, because it will be released by
1089 ldap_memfree() and ldap_memalloc() does not exist. The solution is to
1090 overwrite the EBCDIC buffer with ASCII to return it. */
1091
1092 strcpy(cp, cp2);
1093 free(cp2);
1094 return cp;
1095 }
1096
1097 char *
Curl_ldap_first_attribute_a(void * ld,LDAPMessage * entry,BerElement ** berptr)1098 Curl_ldap_first_attribute_a(void *ld,
1099 LDAPMessage *entry, BerElement **berptr)
1100 {
1101 int i;
1102 char *cp;
1103 char *cp2;
1104
1105 cp = ldap_first_attribute(ld, entry, berptr);
1106
1107 if(!cp)
1108 return cp;
1109
1110 i = strlen(cp);
1111
1112 cp2 = malloc(i + 1);
1113 if(!cp2)
1114 return cp2;
1115
1116 QadrtConvertE2A(cp2, cp, i, i);
1117 cp2[i] = '\0';
1118
1119 /* No way to allocate a buffer here, because it will be released by
1120 ldap_memfree() and ldap_memalloc() does not exist. The solution is to
1121 overwrite the EBCDIC buffer with ASCII to return it. */
1122
1123 strcpy(cp, cp2);
1124 free(cp2);
1125 return cp;
1126 }
1127
1128 char *
Curl_ldap_next_attribute_a(void * ld,LDAPMessage * entry,BerElement * berptr)1129 Curl_ldap_next_attribute_a(void *ld,
1130 LDAPMessage *entry, BerElement *berptr)
1131 {
1132 int i;
1133 char *cp;
1134 char *cp2;
1135
1136 cp = ldap_next_attribute(ld, entry, berptr);
1137
1138 if(!cp)
1139 return cp;
1140
1141 i = strlen(cp);
1142
1143 cp2 = malloc(i + 1);
1144 if(!cp2)
1145 return cp2;
1146
1147 QadrtConvertE2A(cp2, cp, i, i);
1148 cp2[i] = '\0';
1149
1150 /* No way to allocate a buffer here, because it will be released by
1151 ldap_memfree() and ldap_memalloc() does not exist. The solution is to
1152 overwrite the EBCDIC buffer with ASCII to return it. */
1153
1154 strcpy(cp, cp2);
1155 free(cp2);
1156 return cp;
1157 }
1158
1159 #endif /* CURL_DISABLE_LDAP */
1160
1161 static int
sockaddr2ebcdic(struct sockaddr_storage * dstaddr,const struct sockaddr * srcaddr,int srclen)1162 sockaddr2ebcdic(struct sockaddr_storage *dstaddr,
1163 const struct sockaddr *srcaddr, int srclen)
1164 {
1165 const struct sockaddr_un *srcu;
1166 struct sockaddr_un *dstu;
1167 unsigned int i;
1168 unsigned int dstsize;
1169
1170 /* Convert a socket address to job CCSID, if needed. */
1171
1172 if(!srcaddr || srclen < offsetof(struct sockaddr, sa_family) +
1173 sizeof(srcaddr->sa_family) || srclen > sizeof(*dstaddr)) {
1174 errno = EINVAL;
1175 return -1;
1176 }
1177
1178 memcpy((char *) dstaddr, (char *) srcaddr, srclen);
1179
1180 switch(srcaddr->sa_family) {
1181
1182 case AF_UNIX:
1183 srcu = (const struct sockaddr_un *) srcaddr;
1184 dstu = (struct sockaddr_un *) dstaddr;
1185 dstsize = sizeof(*dstaddr) - offsetof(struct sockaddr_un, sun_path);
1186 srclen -= offsetof(struct sockaddr_un, sun_path);
1187 i = QadrtConvertA2E(dstu->sun_path, srcu->sun_path, dstsize - 1, srclen);
1188 dstu->sun_path[i] = '\0';
1189 srclen = i + offsetof(struct sockaddr_un, sun_path);
1190 }
1191
1192 return srclen;
1193 }
1194
1195
1196 static int
sockaddr2ascii(struct sockaddr * dstaddr,int dstlen,const struct sockaddr_storage * srcaddr,int srclen)1197 sockaddr2ascii(struct sockaddr *dstaddr, int dstlen,
1198 const struct sockaddr_storage *srcaddr, int srclen)
1199 {
1200 const struct sockaddr_un *srcu;
1201 struct sockaddr_un *dstu;
1202 unsigned int dstsize;
1203
1204 /* Convert a socket address to ASCII, if needed. */
1205
1206 if(!srclen)
1207 return 0;
1208 if(srclen > dstlen)
1209 srclen = dstlen;
1210 if(!srcaddr || srclen < 0) {
1211 errno = EINVAL;
1212 return -1;
1213 }
1214
1215 memcpy((char *) dstaddr, (char *) srcaddr, srclen);
1216
1217 if(srclen >= offsetof(struct sockaddr_storage, ss_family) +
1218 sizeof(srcaddr->ss_family)) {
1219 switch(srcaddr->ss_family) {
1220
1221 case AF_UNIX:
1222 srcu = (const struct sockaddr_un *) srcaddr;
1223 dstu = (struct sockaddr_un *) dstaddr;
1224 dstsize = dstlen - offsetof(struct sockaddr_un, sun_path);
1225 srclen -= offsetof(struct sockaddr_un, sun_path);
1226 if(dstsize > 0 && srclen > 0) {
1227 srclen = QadrtConvertE2A(dstu->sun_path, srcu->sun_path,
1228 dstsize - 1, srclen);
1229 dstu->sun_path[srclen] = '\0';
1230 }
1231 srclen += offsetof(struct sockaddr_un, sun_path);
1232 }
1233 }
1234
1235 return srclen;
1236 }
1237
1238 int
Curl_os400_connect(int sd,struct sockaddr * destaddr,int addrlen)1239 Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen)
1240 {
1241 int i;
1242 struct sockaddr_storage laddr;
1243
1244 i = sockaddr2ebcdic(&laddr, destaddr, addrlen);
1245
1246 if(i < 0)
1247 return -1;
1248
1249 return connect(sd, (struct sockaddr *) &laddr, i);
1250 }
1251
1252 int
Curl_os400_bind(int sd,struct sockaddr * localaddr,int addrlen)1253 Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen)
1254 {
1255 int i;
1256 struct sockaddr_storage laddr;
1257
1258 i = sockaddr2ebcdic(&laddr, localaddr, addrlen);
1259
1260 if(i < 0)
1261 return -1;
1262
1263 return bind(sd, (struct sockaddr *) &laddr, i);
1264 }
1265
1266 int
Curl_os400_sendto(int sd,char * buffer,int buflen,int flags,const struct sockaddr * dstaddr,int addrlen)1267 Curl_os400_sendto(int sd, char *buffer, int buflen, int flags,
1268 const struct sockaddr *dstaddr, int addrlen)
1269 {
1270 int i;
1271 struct sockaddr_storage laddr;
1272
1273 i = sockaddr2ebcdic(&laddr, dstaddr, addrlen);
1274
1275 if(i < 0)
1276 return -1;
1277
1278 return sendto(sd, buffer, buflen, flags, (struct sockaddr *) &laddr, i);
1279 }
1280
1281 int
Curl_os400_recvfrom(int sd,char * buffer,int buflen,int flags,struct sockaddr * fromaddr,int * addrlen)1282 Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags,
1283 struct sockaddr *fromaddr, int *addrlen)
1284 {
1285 int rcvlen;
1286 struct sockaddr_storage laddr;
1287 int laddrlen = sizeof(laddr);
1288
1289 if(!fromaddr || !addrlen || *addrlen <= 0)
1290 return recvfrom(sd, buffer, buflen, flags, fromaddr, addrlen);
1291
1292 laddr.ss_family = AF_UNSPEC; /* To detect if unused. */
1293 rcvlen = recvfrom(sd, buffer, buflen, flags,
1294 (struct sockaddr *) &laddr, &laddrlen);
1295
1296 if(rcvlen < 0)
1297 return rcvlen;
1298
1299 if(laddr.ss_family == AF_UNSPEC)
1300 laddrlen = 0;
1301 else {
1302 laddrlen = sockaddr2ascii(fromaddr, *addrlen, &laddr, laddrlen);
1303 if(laddrlen < 0)
1304 return laddrlen;
1305 }
1306 *addrlen = laddrlen;
1307 return rcvlen;
1308 }
1309
1310 int
Curl_os400_getpeername(int sd,struct sockaddr * addr,int * addrlen)1311 Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen)
1312 {
1313 struct sockaddr_storage laddr;
1314 int laddrlen = sizeof(laddr);
1315 int retcode = getpeername(sd, (struct sockaddr *) &laddr, &laddrlen);
1316
1317 if(!retcode) {
1318 laddrlen = sockaddr2ascii(addr, *addrlen, &laddr, laddrlen);
1319 if(laddrlen < 0)
1320 return laddrlen;
1321 *addrlen = laddrlen;
1322 }
1323
1324 return retcode;
1325 }
1326
1327 int
Curl_os400_getsockname(int sd,struct sockaddr * addr,int * addrlen)1328 Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen)
1329 {
1330 struct sockaddr_storage laddr;
1331 int laddrlen = sizeof(laddr);
1332 int retcode = getsockname(sd, (struct sockaddr *) &laddr, &laddrlen);
1333
1334 if(!retcode) {
1335 laddrlen = sockaddr2ascii(addr, *addrlen, &laddr, laddrlen);
1336 if(laddrlen < 0)
1337 return laddrlen;
1338 *addrlen = laddrlen;
1339 }
1340
1341 return retcode;
1342 }
1343
1344
1345 #ifdef HAVE_LIBZ
1346 const char *
Curl_os400_zlibVersion(void)1347 Curl_os400_zlibVersion(void)
1348 {
1349 return set_thread_string(LK_ZLIB_VERSION, zlibVersion());
1350 }
1351
1352
1353 int
Curl_os400_inflateInit_(z_streamp strm,const char * version,int stream_size)1354 Curl_os400_inflateInit_(z_streamp strm, const char *version, int stream_size)
1355 {
1356 z_const char *msgb4 = strm->msg;
1357 int ret;
1358
1359 ret = inflateInit(strm);
1360
1361 if(strm->msg != msgb4)
1362 strm->msg = set_thread_string(LK_ZLIB_MSG, strm->msg);
1363
1364 return ret;
1365 }
1366
1367 int
Curl_os400_inflateInit2_(z_streamp strm,int windowBits,const char * version,int stream_size)1368 Curl_os400_inflateInit2_(z_streamp strm, int windowBits,
1369 const char *version, int stream_size)
1370 {
1371 z_const char *msgb4 = strm->msg;
1372 int ret;
1373
1374 ret = inflateInit2(strm, windowBits);
1375
1376 if(strm->msg != msgb4)
1377 strm->msg = set_thread_string(LK_ZLIB_MSG, strm->msg);
1378
1379 return ret;
1380 }
1381
1382 int
Curl_os400_inflate(z_streamp strm,int flush)1383 Curl_os400_inflate(z_streamp strm, int flush)
1384 {
1385 z_const char *msgb4 = strm->msg;
1386 int ret;
1387
1388 ret = inflate(strm, flush);
1389
1390 if(strm->msg != msgb4)
1391 strm->msg = set_thread_string(LK_ZLIB_MSG, strm->msg);
1392
1393 return ret;
1394 }
1395
1396 int
Curl_os400_inflateEnd(z_streamp strm)1397 Curl_os400_inflateEnd(z_streamp strm)
1398 {
1399 z_const char *msgb4 = strm->msg;
1400 int ret;
1401
1402 ret = inflateEnd(strm);
1403
1404 if(strm->msg != msgb4)
1405 strm->msg = set_thread_string(LK_ZLIB_MSG, strm->msg);
1406
1407 return ret;
1408 }
1409
1410 #endif
1411