Fix Linux rpc problems with new glibc
[oweals/cde.git] / cde / lib / csa / connection.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: connection.c /main/4 1999/10/14 17:47:12 mgreess $ */
24 /*
25  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
26  *  (c) Copyright 1993, 1994 International Business Machines Corp.
27  *  (c) Copyright 1993, 1994 Novell, Inc.
28  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
29  */
30
31 /*
32  * This file manages server connections.
33  */
34
35 #include <EUSCompat.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #ifdef HPUX
43 #include <sys/resource.h>
44 #endif
45 #include "connection.h"
46 #include "rtable2.h"
47 #include "rtable3.h"
48 #include "rtable4.h"
49 #include "cm.h"
50 #include "debug.h"
51 #include "agent.h"
52 #include "convert2-4.h"
53 #include "convert3-4.h"
54 #include "rpccalls.h"
55
56 #ifdef HPUX
57 #define MAX_COUNT       10
58 #else
59 #define MAX_COUNT       40
60 #endif
61
62 static struct timeval timeout_tv;
63 static struct timeval retry_tv;
64 static AUTH *unix_credential = NULL;
65 static tcp_count = 0;
66 static cl_count = 0;
67 static _DtCm_Client_Info *client_cache_head = NULL;
68 static _DtCm_Client_Info *client_cache_tail = NULL;
69
70 /*****************************************************************************
71  * forward declaration of static functions.
72  *****************************************************************************/
73 static void create_auth(CLIENT *cl);
74 static void destroy_auth(CLIENT *cl);
75 static _DtCm_Client_Info * get_client_info(char *host, int version);
76 static void destroy_target_list(_DtCm_Target_List *tlist);
77 static void destroy_client_info(_DtCm_Client_Info *ci);
78 static void insert_client_info(_DtCm_Client_Info *ci);
79 static void delete_client_info(_DtCm_Client_Info *oldci);
80 static void cleanup_some_connection(_DtCm_Client_Info *dontclose);
81 static void check_registration(_DtCm_Connection *conn);
82 static _DtCm_Client_Info * get_new_client_handle(_DtCm_Connection *conn);
83 static CSA_return_code get_client_handle(const char *host, const u_long prognum,
84                         u_long *vers_outp, const u_long vers_low,
85                         const u_long vers_high, char *nettype,
86                         CLIENT **clnt);
87 static CSA_return_code regstat4_to_dtcmstatus(Registration_Status_4 stat4);
88
89 extern CSA_return_code
90 _DtCm_create_udp_client(
91         char *host,
92         int version,
93         int timeout,
94         _DtCm_Client_Info **clnt)
95 {
96         CSA_return_code stat;
97         _DtCm_Client_Info       *ci;
98         u_long          vers_out;
99         CLIENT          *cl=NULL;
100
101         if (host == NULL || clnt == NULL)
102                 return (CSA_E_INVALID_PARAMETER);
103
104         /* if client info is found, we have at least the udp handle */
105         if (((*clnt) = get_client_info(host, version)) != NULL) {
106                 return (CSA_SUCCESS);
107         }
108
109 #if defined(SunOS)
110         cl = clnt_create_vers(host, TABLEPROG, 
111                 &vers_out, TABLEVERS_2, version, "udp");
112         if (cl==NULL) {
113                 _DtCm_print_errmsg(clnt_spcreateerror(host));
114                 return (_DtCm_clntstat_to_csastat(rpc_createerr.cf_stat));
115         }
116 #else
117         stat = get_client_handle(host, (u_int)TABLEPROG, &vers_out, TABLEVERS_2,
118                 version, "udp", &cl);
119         if (stat != CSA_SUCCESS)
120                 return (stat);
121 #endif
122
123         /* if version is lower than requested, check the list again */
124         if (vers_out < version) {
125                 if ((ci = get_client_info(host, vers_out)) != NULL) {
126                         clnt_destroy(cl);
127                         *clnt = ci;
128                         return (CSA_SUCCESS);
129                 }
130         }
131
132         create_auth(cl);
133
134         /* Adjust Timeout */
135         if (timeout==0) timeout = _DtCM_DEFAULT_TIMEOUT;
136         timeout_tv.tv_sec =  timeout;
137         timeout_tv.tv_usec = 0;
138         clnt_control(cl, CLSET_TIMEOUT, (char*)&timeout_tv);            
139
140         /*      UDP only!
141                 time rpc waits for server to reply before retransmission =
142                 'timeout'. since the retry timeout is set to timeout + 10;
143                 this guarantees there won't
144                 be any retransmisssions resulting in duplicate 
145                 transactions in the database.
146         */
147
148         retry_tv.tv_sec =  timeout + 10;
149         retry_tv.tv_usec = 0;
150         clnt_control(cl, CLSET_RETRY_TIMEOUT, (char*)&retry_tv);
151
152         if ((ci  = (_DtCm_Client_Info *)calloc(1, sizeof(_DtCm_Client_Info))) == NULL) {
153                 destroy_auth(cl);
154                 clnt_destroy(cl);
155                 return (CSA_E_INSUFFICIENT_MEMORY);
156         }
157
158         if ((ci->host = strdup(host)) == NULL) {
159                 destroy_auth(cl);
160                 clnt_destroy(cl);
161                 free(ci);
162                 return (CSA_E_INSUFFICIENT_MEMORY);
163         }
164
165         ci->udpcl = cl;
166         ci->vers_out = vers_out;
167         insert_client_info(ci);
168         *clnt = ci;
169         return (CSA_SUCCESS);
170 }
171
172 /*
173  * Creates tcp client handle.  Used for calls that potentially return
174  * large amount of data.  If it fails to create a tcp client handle,
175  * a udp client handle will be returned.
176  */
177 extern CSA_return_code
178 _DtCm_create_tcp_client(
179         char *host,
180         int version,
181         int timeout,
182         _DtCm_Client_Info **clnt)
183 {
184         CSA_return_code stat;
185         _DtCm_Client_Info       *ci;
186         u_long          vers_out;
187         CLIENT          *cl=NULL;
188
189         if (host == NULL || clnt == NULL)
190                 return (CSA_E_INVALID_PARAMETER);
191
192         /* Get a udp client handle.  This serves two purposes:             */ 
193         /* - to get a udp handle for an old server which talks only udp    */
194         /* - to invoke a server through inetd since only udp is registered.*/
195
196         if ((stat = _DtCm_create_udp_client(host, version, timeout, &ci))
197             != CSA_SUCCESS) {
198                 return (stat);
199         } else if (ci->tcpcl) {
200                 *clnt = ci;
201                 return (CSA_SUCCESS);
202         } else {
203                 /* create tcp connection */
204 #if defined(SunOS)
205                 cl = clnt_create_vers(host, TABLEPROG, &vers_out,
206                         TABLEVERS_2, version, "tcp");
207 #else
208                 stat = get_client_handle(host, (u_int)TABLEPROG, &vers_out,
209                         TABLEVERS_2, version, "tcp", &cl);
210 #endif
211
212                 /* if can't create tcp connection, use udp */
213                 if (cl==NULL) {
214                         _DtCm_print_errmsg(clnt_spcreateerror(host));
215                         *clnt = ci;
216                         return (CSA_SUCCESS);
217                 }
218
219                 create_auth(cl);
220
221                 /* Adjust Timeout */
222                 if (timeout==0) timeout = _DtCM_DEFAULT_TIMEOUT;
223                 timeout_tv.tv_sec =  timeout;
224                 timeout_tv.tv_usec = 0;
225                 clnt_control(cl, CLSET_TIMEOUT, (char*)&timeout_tv);            
226
227                 /* dont need to set vers_out since it should
228                  * be the same as that of the udp transport
229                  */
230                 ci->tcpcl = cl;
231                 if (++tcp_count > MAX_COUNT)
232                         /* clean up tcp connections */
233                         cleanup_some_connection(ci);
234                 *clnt = ci;
235                 return (CSA_SUCCESS);
236         }
237 }
238
239 /*
240  * Used instead of clnt_call by rtableX_clnt.c
241  *
242  * Might need locking for the client handle here since
243  * it might be purged if something's wrong
244  */
245 extern enum clnt_stat
246 _DtCm_clnt_call(
247         _DtCm_Connection *conn,
248         u_long proc,
249         xdrproc_t inproc,
250         caddr_t in,
251         xdrproc_t outproc,
252         caddr_t out,
253         struct timeval tout)
254 {
255         _DtCm_Client_Info       *ci;
256         _DtCm_Transport_Type    ttype;
257         enum clnt_stat status = RPC_FAILED;
258         int retry = conn->retry;
259
260         while (B_TRUE) {
261                 if (conn->ci == NULL)
262                         break;
263                 else {
264                         ci = conn->ci;
265                         ci->last_used = time(0);
266                 }
267
268                 if (conn->use == udp_transport || ci->tcpcl == NULL)
269                         ttype = udp_transport;
270                 else
271                         ttype = tcp_transport;
272
273                 status = clnt_call((ttype == tcp_transport ? ci->tcpcl :
274                                 ci->udpcl), proc, inproc, in,
275                                 outproc, out, tout);
276
277                 if ((ttype == udp_transport && status == RPC_TIMEDOUT) ||
278                      (status == RPC_CANTRECV)) {
279
280                         if (retry) {
281                                 retry--;
282
283                                 /* don't retry when stat is RPC_TIMEDOUT
284                                  * and transpart is tcp since if the server
285                                  * is down, stat would be something else
286                                  * like RPC_CANTRECV
287                                  */
288
289                                 /* get new client handle */
290                                 if (get_new_client_handle(conn) == NULL)
291                                         break;
292
293                         } else {
294                                 /* purge the client handle */
295                                 delete_client_info(conn->ci);
296                                 conn->ci = NULL;
297                                 break;
298                         }
299                 } else
300                         break;
301         }
302
303         if (status != RPC_SUCCESS && conn->ci != NULL) {
304                 _DtCm_print_errmsg(clnt_sperror((ttype == tcp_transport ? ci->tcpcl :
305                                 ci->udpcl), ci->host));
306         }
307         conn->stat = status;
308         return status;
309 }
310
311 extern CSA_return_code
312 _DtCm_add_registration(
313         _DtCm_Client_Info *ci,
314         char *cal,
315         unsigned long update_type)
316 {
317         _DtCm_Target_List *listp, *prev;
318         _DtCm_Target_List *listitem;
319         int result;
320
321         if (ci == NULL || cal == NULL)
322                 return (CSA_E_INVALID_PARAMETER);
323
324         for (listp = prev = ci->tlist; listp != NULL;
325              prev = listp, listp = listp->next) {
326                 if ((result = strcmp(listp->cal, cal)) == 0) {
327                         /* registered already */
328                         return (CSA_SUCCESS);
329                 } else if (result > 0)
330                         break;
331         }
332
333         /* register the first time, insert in list in ascending order */
334         if ((listitem = (_DtCm_Target_List *)calloc(1, sizeof(_DtCm_Target_List))) == NULL)
335                 return (CSA_E_INSUFFICIENT_MEMORY);
336
337         if ((listitem->cal = strdup(cal)) == NULL) {
338                 free(listitem);
339                 return (CSA_E_INSUFFICIENT_MEMORY);
340         }
341
342         listitem->update_type = update_type;
343
344         if (prev == NULL || listp == prev)
345                 ci->tlist = listitem;
346         else
347                 prev->next = listitem;
348         listitem->next = listp;
349
350         ci->nregistered++;
351
352         return (CSA_SUCCESS);
353 }
354
355 extern void
356 _DtCm_remove_registration(_DtCm_Client_Info *ci, char *cal)
357 {
358         _DtCm_Target_List *listp, *prev;
359         _DtCm_Client_Info *c;
360         int result;
361
362         if (cal == NULL) return;
363
364         /* if found, just increment the number of registration */
365         for (listp = prev = ci->tlist; listp != NULL;
366              prev = listp, listp = listp->next) {
367                 if ((result = strcmp(listp->cal, cal)) == 0) {
368                         if (listp == prev)
369                                 ci->tlist = listp->next;
370                         else
371                                 prev->next = listp->next;
372
373                         /* free target item */
374                         free(listp->cal);
375                         free(listp);
376
377                         /*
378                          * if no calendar is registered, close tcp connection
379                          */
380                         if (--(ci->nregistered) == 0) {
381                                 if (ci->tcpcl) {
382                                         destroy_auth(ci->tcpcl);
383                                         clnt_destroy(ci->tcpcl);
384                                         ci->tcpcl = NULL;
385                                         tcp_count--;
386                                 }
387
388                                 /* find other tcp connection for the
389                                  * same host
390                                  */
391                                 for (c = client_cache_head; c != NULL;
392                                     c = c->next) {
393                                         if ((result = strcmp(c->host,
394                                             ci->host)) == 0) {
395                                                 if (c->nregistered == 0 &&
396                                                     c->tcpcl) {
397                                                         destroy_auth(c->tcpcl);
398                                                         clnt_destroy(c->tcpcl);
399                                                         c->tcpcl = NULL;
400                                                         tcp_count--;
401                                                 }
402                                         } else if (result > 0)
403                                                 break;
404                                 }
405                         }
406                         return;
407                 } else if (result > 0)
408                         break;
409         }
410         /* not found; impossible */
411 }
412
413 extern CSA_return_code
414 _DtCm_get_server_rpc_version(char *host, int *version)
415 {
416         CSA_return_code stat;
417         _DtCm_Client_Info *ci;
418
419         if (host == NULL) {
420                 return (CSA_E_INVALID_PARAMETER);
421         }
422
423         if ((stat = _DtCm_create_tcp_client(host, TABLEVERS,
424             _DtCM_INITIAL_TIMEOUT, &ci)) == CSA_SUCCESS)
425                 *version = ci->vers_out;
426
427         return (stat);
428 }
429
430 extern CSA_return_code
431 _DtCm_clntstat_to_csastat(enum clnt_stat clntstat)
432 {
433         switch (clntstat) {
434 #if defined(SunOS)
435         case RPC_N2AXLATEFAILURE:
436 #endif
437         case RPC_UNKNOWNHOST:
438                 return (CSA_X_DT_E_INVALID_SERVER_LOCATION);
439         case RPC_PROGNOTREGISTERED:
440                 return (CSA_X_DT_E_SERVICE_NOT_REGISTERED);
441         case RPC_TIMEDOUT:
442                 return (CSA_X_DT_E_SERVER_TIMEOUT);
443         default:
444                 return (CSA_E_SERVICE_UNAVAILABLE);
445         }
446 }
447
448 /*****************************************************************************
449  * static functions used within the file
450  *****************************************************************************/
451
452 static void
453 create_auth(CLIENT *cl)
454 {
455         /* Always cache the Unix style credentials. */
456         if (unix_credential == NULL)
457 #if defined(SunOS)
458                 unix_credential = authsys_create_default ();
459 #else
460                 unix_credential = authunix_create_default ();
461 #endif
462
463         cl->cl_auth = unix_credential;
464 }
465
466 static void
467 destroy_auth(CLIENT *cl)
468 {
469         /* It is a no-op for unix-authentication because we always cache it.
470          * But we have to destroy it when secure RPC is used.
471          */
472 }
473
474 /*
475  * Given a host name, find the _DtCm_Client_Info structure which contains
476  * both udp and tcp handle to the server running in the host.
477  */
478 static _DtCm_Client_Info *
479 get_client_info(char *host, int version)
480 {
481         _DtCm_Client_Info *ci;
482         int result;
483
484         if (host==NULL) return(NULL);
485         for (ci = client_cache_head; ci != NULL; ci = ci->next) {
486                 if ((result = strcmp(ci->host, host)) == 0) {
487                         if (ci->vers_out <= version)
488                                 return(ci); 
489                 } else if (result > 0)
490                         break;
491         }
492         return(NULL);
493 }
494
495 static void
496 destroy_target_list(_DtCm_Target_List *tlist)
497 {
498         _DtCm_Target_List *listp, *listitem;
499
500         for (listp = tlist; listp != NULL; ) {
501                 listitem = listp;
502                 listp = listp->next;
503
504                 if (listitem->cal)
505                         free(listitem->cal);
506                 free(listitem);
507         }
508 }
509
510 static void
511 destroy_client_info(_DtCm_Client_Info *ci)
512 {
513         if (ci==NULL) return;
514
515         if (ci->host != NULL)
516                 free(ci->host);
517         if (ci->tcpcl) {
518                 destroy_auth(ci->tcpcl);
519                 clnt_destroy(ci->tcpcl);
520                 tcp_count--;
521         }
522         if (ci->udpcl) {
523                 destroy_auth(ci->udpcl);
524                 clnt_destroy(ci->udpcl);
525         }
526         destroy_target_list(ci->tlist);
527         free(ci);
528         cl_count--;
529 }
530
531 /*
532  * Dont limit the number of cached connections right now.
533  * Udp client handle does not use up file descriptor only space.
534  * Tcp client handle is kept open only when there's at least one
535  * calendar registered with the host and the user probably won't
536  * be browsing more than 50 calendar at the same time.
537  */
538 static void
539 insert_client_info(_DtCm_Client_Info *ci)
540 {
541         _DtCm_Client_Info *citem;
542
543         if (++cl_count > MAX_COUNT)
544                 cleanup_some_connection(ci);
545
546         /* insert new item alphabetically */
547         for (citem = client_cache_head; citem != NULL; citem = citem->next) {
548                 /* there shouldn't be an entry with the same host name
549                  * if there's, it would be picked up in get_client_info()
550                  */
551                 if (strcmp(citem->host, ci->host) > 0)
552                         break;
553         }
554
555         if (citem == NULL) {
556                 if (client_cache_head == NULL)
557                         client_cache_head = client_cache_tail = ci;
558                 else {
559                         ci->prev = client_cache_tail;
560                         client_cache_tail->next = ci;
561                         client_cache_tail = ci;
562                 }
563         } else {
564                 ci->next = citem;
565                 ci->prev = citem->prev;
566                 if (citem == client_cache_head)
567                         client_cache_head = ci;
568                 else
569                         citem->prev->next = ci;
570                 citem->prev = ci;
571         }
572
573 #ifdef CM_DEBUG
574         fprintf(stderr, "%s: head = %d, tail = %d, newitem = %d\n",
575                 "insert_client_info", client_cache_head,
576                 client_cache_tail, ci);
577         fprintf(stderr, "tcp_count = %d, cl_count = %d\n", tcp_count, cl_count);
578 #endif
579
580 }
581
582 /*
583  * remove the client info structure from the list
584  */
585 static void
586 delete_client_info(_DtCm_Client_Info *oldci)
587 {
588         if (oldci == NULL) return;
589
590         if (oldci == client_cache_head) {
591                 client_cache_head = oldci->next;
592                 if (client_cache_head)
593                         client_cache_head->prev = NULL;
594         } else if (oldci == client_cache_tail) {
595                 client_cache_tail = oldci->prev;
596                 if (client_cache_tail)
597                         client_cache_tail->next = NULL;
598         } else {
599                 oldci->prev->next = oldci->next;
600                 oldci->next->prev = oldci->prev;
601         }
602
603         if (oldci == client_cache_tail)
604                 client_cache_tail = NULL;
605
606         destroy_client_info(oldci);
607
608 #ifdef CM_DEBUG
609         fprintf(stderr, "%s: head = %d, tail = %d, olditem = %d\n",
610                 "delete_client_info", client_cache_head,
611                 client_cache_tail, oldci);
612 #endif
613 }
614
615 /*
616  * Number of open tcp connections reaches the maximum.
617  * This is very unlikely in the normal case since
618  * a tcp connection is kept open if at least one calendar
619  * is registered with the host and a user would not be
620  * browsing a large number of calendars at one time.
621  * However, when a calendar is deselected in the calendar
622  * list on the multi-browser window, a lookup call using
623  * the tcp connection is made after the calendar is
624  * deregistered.  This keeps the tcp connection open
625  * even if that's the last calendar registered with the
626  * host.  This routine is used to clean up such tcp connections.
627  * This is a good time to clean up connections that are not
628  * used for a long time.
629  */
630 static void
631 cleanup_some_connection(_DtCm_Client_Info *dontclose)
632 {
633         _DtCm_Client_Info *ci, *oldci;
634         int total = 0, deleted = 0, done = 0;
635
636         for (ci = client_cache_head; ci != NULL; )
637         {
638                 total++;
639 #ifdef HPUX
640                 /* clean up whole list */
641                 if (ci != dontclose && ci->nregistered == 0) {
642 #else
643
644                 if (ci != dontclose && ci->nregistered == 0 &&
645                     (ci->tcpcl || (!done && ci->tcpcl == NULL) ||
646                      (ci->tcpcl==NULL && (time(NULL) - ci->last_used)>DAYSEC)))
647                 {
648                         if (!done) done = 1;
649 #endif
650
651                         deleted++;
652                         oldci = ci;
653                         ci = ci->next;
654                         delete_client_info(oldci);
655                 } else
656                         ci = ci->next;
657         }
658 #ifdef CM_DEBUG
659         fprintf(stderr, "%s: total = %d, deleted = %d\n",
660                 "cleanup_tcp_connection", total, deleted);
661 #endif
662 }
663
664 /*
665  * check registration
666  * Deergister the first target:
667  * if it succeeded, the old server is still running, just re-register it;
668  * else assume that it's a new server so re-register the whole list again.
669  */
670 static void
671 check_registration(_DtCm_Connection *conn)
672 {
673         _DtCm_Target_List *listp, *prev;
674         _DtCm_Transport_Type olduse;
675         CSA_return_code stat;
676
677         if (conn->ci->tlist == NULL)
678                 return;
679
680         olduse = conn->use;
681         conn->use = udp_transport;
682         conn->retry = B_FALSE;
683         if ((stat = _DtCm_do_unregistration(conn, conn->ci->tlist->cal,
684             conn->ci->tlist->update_type)) == CSA_SUCCESS) {
685                 if (_DtCm_do_registration(conn, conn->ci->tlist->cal,
686                         conn->ci->tlist->update_type) != CSA_SUCCESS)
687                 {
688                         conn->ci->nregistered--;
689                         listp =  conn->ci->tlist;
690                         conn->ci->tlist = listp->next;
691                         free(listp->cal);
692                         free(listp);
693                 }
694         } else if (stat == CSA_E_CALLBACK_NOT_REGISTERED || stat == CSA_E_FAILURE) {
695                 for (listp = prev = conn->ci->tlist; listp != NULL; ) {
696                         if (_DtCm_do_registration(conn, listp->cal,
697                             listp->update_type) != CSA_SUCCESS)
698                         {
699                                 conn->ci->nregistered--;
700                                 if (listp == prev)
701                                         conn->ci->tlist = prev = listp->next;
702                                 else
703                                         prev->next = listp->next;
704                                 /* free target item */
705                                 free(listp->cal);
706                                 free(listp);
707                                 listp = (prev ? prev->next : NULL);
708                         } else {
709                                 prev = listp;
710                                 listp = listp->next;
711                         }
712                 }
713         }
714         conn->use = olduse;
715 }
716
717 static _DtCm_Client_Info *
718 get_new_client_handle(_DtCm_Connection *conn)
719 {
720         CLIENT *cl;
721         int oldver;
722
723         if (conn == NULL) return(NULL);
724
725         oldver = conn->ci->vers_out;
726
727         /* always get a udp client handle first */
728 #if defined(SunOS)
729         cl = clnt_create_vers(conn->ci->host, TABLEPROG, &(conn->ci->vers_out),
730                         TABLEVERS_2, oldver, "udp");
731         if (cl == NULL) {
732                 _DtCm_print_errmsg(clnt_spcreateerror(conn->ci->host));
733         }
734 #else
735         (void) get_client_handle(conn->ci->host, (u_int)TABLEPROG,
736                         &(conn->ci->vers_out), TABLEVERS_2, oldver,
737                         "udp", &cl);
738 #endif
739
740         if (cl == NULL) {
741                 delete_client_info(conn->ci);
742                 conn->ci = NULL;
743                 return(NULL);
744         } else {
745                 create_auth(cl);
746
747                 /* adjust timeout */
748                 timeout_tv.tv_sec = _DtCM_INITIAL_TIMEOUT;
749                 timeout_tv.tv_usec = 0;
750                 clnt_control(cl, CLSET_TIMEOUT, (char *)&timeout_tv);
751                 retry_tv.tv_sec = _DtCM_INITIAL_TIMEOUT + 10;
752                 retry_tv.tv_usec = 0;
753                 clnt_control(cl, CLSET_RETRY_TIMEOUT, (char *)&retry_tv);
754
755                 destroy_auth(conn->ci->udpcl);
756                 clnt_destroy(conn->ci->udpcl);
757                 conn->ci->udpcl = cl;
758         }
759
760         /* check registration */
761         /* if there's anything wrong, nregistered could be zero */
762         check_registration(conn);
763
764         /* ci might be set to NULL if an rpc call failed */
765         if (conn->ci == NULL)
766                 return (NULL);
767
768         /* now deal with tcp handle */
769
770         /* get rid of old handle first */
771         if (conn->ci->tcpcl) {
772                 destroy_auth(conn->ci->tcpcl);
773                 clnt_destroy(conn->ci->tcpcl);
774                 tcp_count--;
775                 conn->ci->tcpcl = NULL;
776         }
777
778         if (conn->use == udp_transport) {
779                 return(conn->ci);
780         } else {
781
782                 /* get a tcp client handle */
783                 oldver = conn->ci->vers_out;
784 #if defined(SunOS)
785                 cl = clnt_create_vers(conn->ci->host, TABLEPROG,
786                         &(conn->ci->vers_out), TABLEVERS_2, oldver, "tcp");
787                 if (cl == NULL)
788                         _DtCm_print_errmsg(clnt_spcreateerror(conn->ci->host));
789 #else
790                 (void) get_client_handle(conn->ci->host, (u_int)TABLEPROG,
791                         &(conn->ci->vers_out), TABLEVERS_2, oldver, "tcp",
792                         &cl);
793 #endif
794
795                 if (cl == NULL) {
796                         conn->ci->vers_out = oldver;
797                         return(NULL);
798                 } else {
799                         create_auth(cl);
800
801                         /* adjust timeout */
802                         timeout_tv.tv_sec = _DtCM_INITIAL_TIMEOUT;
803                         timeout_tv.tv_usec = 0;
804                         clnt_control(cl, CLSET_TIMEOUT, (char *)&timeout_tv);
805
806                         conn->ci->tcpcl = cl;
807                         tcp_count++;
808                         return(conn->ci);
809                 }
810         }
811 }
812
813 /*
814  * Get a client handle to a server that supports the highest
815  * version between the given range.
816  */
817 static CSA_return_code
818 get_client_handle(
819         const char *host,
820         const u_long prognum,
821         u_long *vers_outp,
822         const u_long vers_low,
823         const u_long vers_high,
824         char *nettype,
825         CLIENT **clnt)
826 {
827         CLIENT  *cl;
828         u_int   vers;
829         struct timeval tv;
830         enum clnt_stat status;
831
832 #ifdef HPUX
833         static int bumped = 0;
834         struct rlimit rl;
835
836         if (bumped == 0) {
837                 bumped = 1;
838
839                 /* raise the soft limit of number of file descriptor */
840                 getrlimit(RLIMIT_NOFILE, &rl);
841                 rl.rlim_cur = rl.rlim_max;
842                 setrlimit(RLIMIT_NOFILE, &rl);
843         }
844 #endif
845
846         tv.tv_sec = 1;
847         tv.tv_usec = 0;
848
849         *clnt = NULL;
850         for (vers = vers_high; vers >= vers_low; vers--) {
851 #if defined(__hpux)
852                 if ((cl = clnt_create((char *)host, prognum, vers, nettype)) != NULL) {
853 #else
854
855                 if ((cl = clnt_create(host, prognum, vers, nettype)) != NULL) {
856 #endif
857                         clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
858                         status = clnt_call(cl, 0, (xdrproc_t) xdr_void, 
859                                            (char *)NULL, (xdrproc_t) xdr_void,
860                                            (char *)NULL, tv);
861
862                         if (status == RPC_SUCCESS) {
863                                 *vers_outp = vers;
864                                 *clnt = cl;
865                                 return (CSA_SUCCESS);
866                         } else if (status != RPC_PROGVERSMISMATCH) {
867                                 return (_DtCm_clntstat_to_csastat(status));
868                         }
869                 } else {
870                         _DtCm_print_errmsg(clnt_spcreateerror((char *) host));
871                         return (_DtCm_clntstat_to_csastat(rpc_createerr.cf_stat));
872                 }
873         }
874
875         /* cannot find a server that supports a version in the given range */
876         /* Probably will never get here */
877         return (CSA_E_SERVICE_UNAVAILABLE);
878 }
879
880 static CSA_return_code
881 regstat4_to_dtcmstatus(Registration_Status_4 stat4)
882 {
883         switch (stat4) {
884         case registered_4:
885                 return (CSA_SUCCESS);
886
887         case deregistered_4:
888                 return (CSA_SUCCESS);
889
890         case reg_notable_4:
891                 return (CSA_E_CALENDAR_NOT_EXIST);
892
893         case failed_4:
894         case confused_4:
895                 return (CSA_E_FAILURE);
896         }
897 }
898