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