Back to index

lightning-sunbird  0.9+nobinonly
nbconn.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * A test for nonblocking connect.  Functions tested include PR_Connect,
00040  * PR_Poll, and PR_GetConnectStatus.
00041  *
00042  * The test should be invoked with a host name, for example:
00043  *     nbconn www.netscape.com
00044  * It will do a nonblocking connect to port 80 (HTTP) on that host,
00045  * and when connected, issue the "GET /" HTTP command.
00046  *
00047  * You should run this test in three ways:
00048  * 1. To a known web site, such as www.netscape.com.  The HTML of the
00049  *    top-level page at the web site should be printed.
00050  * 2. To a machine not running a web server at port 80.  This test should
00051  *    fail.  Ideally the error code should be PR_CONNECT_REFUSED_ERROR.
00052  *    But it is possible to return PR_UNKNOWN_ERROR on certain platforms.
00053  * 3. To an unreachable machine, for example, a machine that is off line.
00054  *    The test should fail after the connect times out.  Ideally the
00055  *    error code should be PR_IO_TIMEOUT_ERROR, but it is possible to
00056  *    return PR_UNKNOWN_ERROR on certain platforms.
00057  */
00058 
00059 #include "nspr.h"
00060 #include "plgetopt.h"
00061 #include <stdio.h>
00062 #include <string.h>
00063 
00064 #ifdef XP_MAC
00065 #define printf PR_LogPrint
00066 extern void SetupMacPrintfLog(char *logFile);
00067 static char *hosts[4] = {"cynic", "warp", "gandalf", "neon"};
00068 #endif
00069 
00070 #define SERVER_MAX_BIND_COUNT        100
00071 #define DATA_BUF_SIZE                      256
00072 #define TCP_SERVER_PORT            10000
00073 #define TCP_UNUSED_PORT            211
00074 
00075 typedef struct Server_Param {
00076     PRFileDesc *sp_fd;             /* server port */
00077 } Server_Param;
00078 static void PR_CALLBACK TCP_Server(void *arg);
00079 
00080 int _debug_on;
00081 #define DPRINTF(arg) if (_debug_on) printf arg
00082 
00083 static PRIntn connection_success_test();
00084 static PRIntn connection_failure_test();
00085 
00086 int main(int argc, char **argv)
00087 {
00088     PRHostEnt he;
00089     char buf[1024];
00090     PRNetAddr addr;
00091     PRPollDesc pd;
00092     PRStatus rv;
00093     PRSocketOptionData optData;
00094        const char *hostname = NULL;
00095     PRIntn default_case, n, bytes_read, bytes_sent;
00096        PRInt32 failed_already = 0;
00097 #ifdef XP_MAC
00098        int index;
00099        PRIntervalTime timeout;
00100 #endif
00101 
00102     /*
00103      * -d           debug mode
00104      */
00105 
00106     PLOptStatus os;
00107     PLOptState *opt = PL_CreateOptState(argc, argv, "d");
00108     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
00109     {
00110         if (PL_OPT_BAD == os) continue;
00111         switch (opt->option)
00112         {
00113         case 0:  /* debug mode */
00114             hostname = opt->value;
00115             break;
00116         case 'd':  /* debug mode */
00117             _debug_on = 1;
00118             break;
00119         default:
00120             break;
00121         }
00122     }
00123     PL_DestroyOptState(opt);
00124 
00125 #ifdef XP_MAC
00126        SetupMacPrintfLog("nbconn.log");
00127        for (index=0; index<4; index++) {
00128        argv[1] = hosts[index];
00129        timeout = PR_INTERVAL_NO_TIMEOUT;
00130        if (index == 3)
00131               timeout = PR_SecondsToInterval(10UL);
00132 #endif
00133 
00134 
00135     PR_STDIO_INIT();
00136 #ifndef XP_MAC
00137     if (hostname)
00138               default_case = 0;
00139        else
00140               default_case = 1;
00141 #endif
00142 
00143        if (default_case) {
00144 
00145               /*
00146                * In the default case the following tests are executed:
00147                *     1. successful connection: a server thread accepts a connection
00148                *        from the main thread
00149                *     2. unsuccessful connection: the main thread tries to connect to a
00150                *        non-existent port and expects to get an error
00151                */
00152               rv = connection_success_test();
00153               if (rv == 0)
00154                      rv = connection_failure_test();
00155               return rv;
00156        } else {
00157        PRFileDesc *sock;
00158 
00159               if (PR_GetHostByName(argv[1], buf, sizeof(buf), &he) == PR_FAILURE) {
00160                      printf( "Unknown host: %s\n", argv[1]);
00161                      exit(1);
00162               } else {
00163                      printf( "host: %s\n", buf);
00164               }
00165               PR_EnumerateHostEnt(0, &he, 80, &addr);
00166 
00167               sock = PR_NewTCPSocket();
00168               optData.option = PR_SockOpt_Nonblocking;
00169               optData.value.non_blocking = PR_TRUE;
00170               PR_SetSocketOption(sock, &optData);
00171               rv = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
00172               if (rv == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) {
00173                      printf( "Connect in progress\n");
00174               }
00175 
00176               pd.fd = sock;
00177               pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
00178 #ifndef XP_MAC
00179               n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
00180 #else
00181               n = PR_Poll(&pd, 1, timeout);
00182 #endif
00183               if (n == -1) {
00184                      printf( "PR_Poll failed\n");
00185                      exit(1);
00186               }
00187               printf( "PR_Poll returns %d\n", n);
00188               if (pd.out_flags & PR_POLL_READ) {
00189                      printf( "PR_POLL_READ\n");
00190               }
00191               if (pd.out_flags & PR_POLL_WRITE) {
00192                      printf( "PR_POLL_WRITE\n");
00193               }
00194               if (pd.out_flags & PR_POLL_EXCEPT) {
00195                      printf( "PR_POLL_EXCEPT\n");
00196               }
00197               if (pd.out_flags & PR_POLL_ERR) {
00198                      printf( "PR_POLL_ERR\n");
00199               }
00200               if (pd.out_flags & PR_POLL_NVAL) {
00201                      printf( "PR_POLL_NVAL\n");
00202               }
00203 
00204               if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
00205                      printf("PR_GetConnectStatus: connect succeeded\n");
00206                      /* Mac and Win16 have trouble printing to the console. */
00207 #if !defined(XP_MAC) && !defined(WIN16)
00208                      PR_Write(sock, "GET /\r\n\r\n", 9);
00209                      PR_Shutdown(sock, PR_SHUTDOWN_SEND);
00210                      pd.in_flags = PR_POLL_READ;
00211                      while (1) {
00212                             n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
00213                             printf( "poll returns %d\n", n);
00214                             n = PR_Read(sock, buf, sizeof(buf));
00215                             printf( "read returns %d\n", n);
00216                             if (n <= 0) {
00217                                    break;
00218                             }
00219                             PR_Write(PR_STDOUT, buf, n);
00220                      }
00221 #endif
00222               } else {
00223                      if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
00224                             printf( "PR_GetConnectStatus: connect still in progress\n");
00225                             exit(1);
00226                      }
00227                      printf( "PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
00228                                    PR_GetError(), PR_GetOSError());
00229               }
00230               PR_Close(sock);
00231 #ifdef XP_MAC
00232               } /* end of for loop */
00233 #endif
00234        printf( "PASS\n");
00235        return 0;
00236 
00237        }
00238 }
00239 
00240 
00241 /*
00242  * TCP Server
00243  *    Server Thread
00244  *    Accept a connection from the client and write some data
00245  */
00246 static void PR_CALLBACK
00247 TCP_Server(void *arg)
00248 {
00249     Server_Param *sp = (Server_Param *) arg;
00250     PRFileDesc *sockfd, *newsockfd;
00251        char data_buf[DATA_BUF_SIZE];
00252     PRIntn rv, bytes_read;
00253 
00254        sockfd = sp->sp_fd;
00255        if ((newsockfd = PR_Accept(sockfd, NULL,
00256               PR_INTERVAL_NO_TIMEOUT)) == NULL) {
00257               fprintf(stderr,"ERROR - PR_Accept failed: (%d,%d)\n",
00258                                                                       PR_GetError(), PR_GetOSError());
00259               return;
00260        }
00261        bytes_read = 0;
00262        while (bytes_read != DATA_BUF_SIZE) {
00263               rv = PR_Read(newsockfd, data_buf + bytes_read ,
00264                                                                DATA_BUF_SIZE - bytes_read);
00265               if (rv < 0) {
00266                      fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
00267                                                  PR_GetError(), PR_GetOSError());
00268                      PR_Close(newsockfd);
00269                      return;
00270               }
00271               PR_ASSERT(rv != 0);
00272               bytes_read += rv;
00273        }
00274        DPRINTF(("Bytes read from client - %d\n",bytes_read));
00275        rv = PR_Write(newsockfd, data_buf,DATA_BUF_SIZE);
00276        if (rv < 0) {
00277               fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
00278                                           PR_GetError(), PR_GetOSError());
00279               PR_Close(newsockfd);
00280               return;
00281        }
00282        PR_ASSERT(rv == DATA_BUF_SIZE);
00283        DPRINTF(("Bytes written to client - %d\n",rv));
00284        PR_Close(newsockfd);
00285 }
00286 
00287 
00288 /*
00289  * test for successful connection using a non-blocking socket
00290  */
00291 static PRIntn
00292 connection_success_test()
00293 {
00294        PRFileDesc *sockfd = NULL, *conn_fd = NULL;
00295        PRNetAddr netaddr;
00296        PRInt32 i, rv;
00297     PRPollDesc pd;
00298     PRSocketOptionData optData;
00299        PRThread *thr = NULL;
00300        Server_Param sp;
00301        char send_buf[DATA_BUF_SIZE], recv_buf[DATA_BUF_SIZE];
00302     PRIntn default_case, n, bytes_read, bytes_sent;
00303     PRIntn failed_already = 0;
00304 
00305        /*
00306         * Create a tcp socket
00307         */
00308        if ((sockfd = PR_NewTCPSocket()) == NULL) {
00309               fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
00310               failed_already=1;
00311               goto def_exit;
00312        }
00313        memset(&netaddr, 0 , sizeof(netaddr));
00314        netaddr.inet.family = PR_AF_INET;
00315        netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
00316        netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
00317        /*
00318         * try a few times to bind server's address, if addresses are in
00319         * use
00320         */
00321        i = 0;
00322        while (PR_Bind(sockfd, &netaddr) < 0) {
00323               if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
00324                      netaddr.inet.port += 2;
00325                      if (i++ < SERVER_MAX_BIND_COUNT)
00326                             continue;
00327               }
00328               fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
00329                                                                PR_GetError(), PR_GetOSError());
00330               failed_already=1;
00331               goto def_exit;
00332        }
00333 
00334        if (PR_Listen(sockfd, 32) < 0) {
00335               fprintf(stderr,"ERROR - PR_Listen failed: (%d,%d)\n",
00336                                                                PR_GetError(), PR_GetOSError());
00337               failed_already=1;
00338               goto def_exit;
00339        }
00340 
00341        if (PR_GetSockName(sockfd, &netaddr) < 0) {
00342               fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
00343                                                                PR_GetError(), PR_GetOSError());
00344               failed_already=1;
00345               goto def_exit;
00346        }
00347        if ((conn_fd = PR_NewTCPSocket()) == NULL) {
00348               fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
00349               failed_already=1;
00350               goto def_exit;
00351        }
00352        optData.option = PR_SockOpt_Nonblocking;
00353        optData.value.non_blocking = PR_TRUE;
00354        PR_SetSocketOption(conn_fd, &optData);
00355        rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
00356        if (rv == PR_FAILURE) {
00357               if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
00358                      DPRINTF(("Connect in progress\n"));
00359               } else  {
00360                      fprintf(stderr,"Error - PR_Connect failed: (%d, %d)\n",
00361                                                                PR_GetError(), PR_GetOSError());
00362                      failed_already=1;
00363                      goto def_exit;
00364               }
00365        }
00366        /*
00367         * Now create a thread to accept a connection
00368         */
00369        sp.sp_fd = sockfd;
00370        thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void *)&sp, 
00371                      PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
00372        if (thr == NULL) {
00373               fprintf(stderr,"Error - PR_CreateThread failed: (%d,%d)\n",
00374                                                                PR_GetError(), PR_GetOSError());
00375               failed_already=1;
00376               goto def_exit;
00377        }
00378        DPRINTF(("Created TCP_Server thread [0x%x]\n",thr));
00379        pd.fd = conn_fd;
00380        pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
00381 #ifndef XP_MAC
00382        n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
00383 #else
00384        n = PR_Poll(&pd, 1, timeout);
00385 #endif
00386        if (n == -1) {
00387               fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
00388                                                                PR_GetError(), PR_GetOSError());
00389               failed_already=1;
00390               goto def_exit;
00391        }
00392        if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
00393               PRInt32 rv;
00394 
00395               DPRINTF(("Connection successful\n"));
00396 
00397               /*
00398                * Write some data, read it back and check data integrity to
00399                * make sure the connection is good
00400                */
00401               pd.in_flags = PR_POLL_WRITE;
00402               bytes_sent = 0;
00403               memset(send_buf, 'a', DATA_BUF_SIZE);
00404               while (bytes_sent != DATA_BUF_SIZE) {
00405                      rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
00406                      if (rv < 0) {
00407                             fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
00408                                                         PR_GetError(), PR_GetOSError());
00409                             failed_already=1;
00410                             goto def_exit;
00411                      }
00412                      PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_WRITE));
00413                      rv = PR_Write(conn_fd, send_buf + bytes_sent,
00414                                                                       DATA_BUF_SIZE - bytes_sent);
00415                      if (rv < 0) {
00416                             fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
00417                                                         PR_GetError(), PR_GetOSError());
00418                             failed_already=1;
00419                             goto def_exit;
00420                      }
00421                      PR_ASSERT(rv > 0);
00422                      bytes_sent += rv;
00423               }
00424               DPRINTF(("Bytes written to server - %d\n",bytes_sent));
00425               PR_Shutdown(conn_fd, PR_SHUTDOWN_SEND);
00426               pd.in_flags = PR_POLL_READ;
00427               bytes_read = 0;
00428               memset(recv_buf, 0, DATA_BUF_SIZE);
00429               while (bytes_read != DATA_BUF_SIZE) {
00430                      rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
00431                      if (rv < 0) {
00432                             fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
00433                                                         PR_GetError(), PR_GetOSError());
00434                             failed_already=1;
00435                             goto def_exit;
00436                      }
00437                      PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_READ));
00438                      rv = PR_Read(conn_fd, recv_buf + bytes_read ,
00439                                                                       DATA_BUF_SIZE - bytes_read);
00440                      if (rv < 0) {
00441                             fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
00442                                                         PR_GetError(), PR_GetOSError());
00443                             failed_already=1;
00444                             goto def_exit;
00445                      }
00446                      PR_ASSERT(rv != 0);
00447                      bytes_read += rv;
00448               }
00449               DPRINTF(("Bytes read from server - %d\n",bytes_read));
00450               /*
00451                * verify the data read
00452                */
00453               if (memcmp(send_buf, recv_buf, DATA_BUF_SIZE) != 0) {
00454                      fprintf(stderr,"ERROR - data corruption\n");
00455                      failed_already=1;
00456                      goto def_exit;
00457               }
00458               DPRINTF(("Data integrity verified\n"));
00459        } else {
00460               fprintf(stderr,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
00461                             PR_GetError(), PR_GetOSError());
00462               failed_already = 1;
00463               goto def_exit;
00464        }
00465 def_exit:
00466        if (thr) {
00467               PR_JoinThread(thr);
00468               thr = NULL;
00469        }
00470        if (sockfd) {
00471               PR_Close(sockfd);
00472               sockfd = NULL;
00473        }
00474        if (conn_fd) {
00475               PR_Close(conn_fd);
00476               conn_fd = NULL;
00477        }
00478        if (failed_already)
00479               return 1;
00480        else
00481               return 0;
00482 
00483 }
00484 
00485 /*
00486  * test for connection to a non-existent port using a non-blocking socket
00487  */
00488 static PRIntn
00489 connection_failure_test()
00490 {
00491        PRFileDesc *sockfd = NULL, *conn_fd = NULL;
00492        PRNetAddr netaddr;
00493        PRInt32 i, rv;
00494     PRPollDesc pd;
00495     PRSocketOptionData optData;
00496     PRIntn n, failed_already = 0;
00497 
00498        /*
00499         * Create a tcp socket
00500         */
00501        if ((sockfd = PR_NewTCPSocket()) == NULL) {
00502               fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
00503               failed_already=1;
00504               goto def_exit;
00505        }
00506        memset(&netaddr, 0 , sizeof(netaddr));
00507        netaddr.inet.family = PR_AF_INET;
00508        netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
00509        netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
00510        /*
00511         * try a few times to bind server's address, if addresses are in
00512         * use
00513         */
00514        i = 0;
00515        while (PR_Bind(sockfd, &netaddr) < 0) {
00516               if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
00517                      netaddr.inet.port += 2;
00518                      if (i++ < SERVER_MAX_BIND_COUNT)
00519                             continue;
00520               }
00521               fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
00522                                                                PR_GetError(), PR_GetOSError());
00523               failed_already=1;
00524               goto def_exit;
00525        }
00526 
00527        if (PR_GetSockName(sockfd, &netaddr) < 0) {
00528               fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
00529                                                                PR_GetError(), PR_GetOSError());
00530               failed_already=1;
00531               goto def_exit;
00532        }
00533 #ifdef AIX
00534        /*
00535         * On AIX, set to unused/reserved port
00536         */
00537        netaddr.inet.port = PR_htons(TCP_UNUSED_PORT);
00538 #endif
00539        if ((conn_fd = PR_NewTCPSocket()) == NULL) {
00540               fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
00541               failed_already=1;
00542               goto def_exit;
00543        }
00544        optData.option = PR_SockOpt_Nonblocking;
00545        optData.value.non_blocking = PR_TRUE;
00546        PR_SetSocketOption(conn_fd, &optData);
00547        rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
00548        if (rv == PR_FAILURE) {
00549               DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n",
00550                                                                PR_GetError(), PR_GetOSError()));
00551        } else {
00552               PR_ASSERT(rv == PR_SUCCESS);
00553               fprintf(stderr,"Error - PR_Connect succeeded, expected to fail\n");
00554               failed_already=1;
00555               goto def_exit;
00556        }
00557        pd.fd = conn_fd;
00558        pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
00559 #ifndef XP_MAC
00560        n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
00561 #else
00562        n = PR_Poll(&pd, 1, timeout);
00563 #endif
00564        if (n == -1) {
00565               fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
00566                                                                PR_GetError(), PR_GetOSError());
00567               failed_already=1;
00568               goto def_exit;
00569        }
00570        if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
00571               PRInt32 rv;
00572               fprintf(stderr,"PR_GetConnectStatus succeeded, expected to fail\n");
00573               failed_already = 1;
00574               goto def_exit;
00575        }
00576        rv = PR_GetError();
00577        DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv));
00578 def_exit:
00579        if (sockfd) {
00580               PR_Close(sockfd);
00581               sockfd = NULL;
00582        }
00583        if (conn_fd) {
00584               PR_Close(conn_fd);
00585               conn_fd = NULL;
00586        }
00587        if (failed_already)
00588               return 1;
00589        else
00590               return 0;
00591 
00592 }