00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067 #include "lwip/opt.h"
00068
00069 #if PPP_SUPPORT
00070
00071 #include "ppp.h"
00072 #include "pppdebug.h"
00073
00074 #include "fsm.h"
00075 #include "lcp.h"
00076 #include "pap.h"
00077 #include "chap.h"
00078 #include "auth.h"
00079 #include "ipcp.h"
00080
00081 #if CBCP_SUPPORT
00082 #include "cbcp.h"
00083 #endif
00084
00085 #include <string.h>
00086
00087
00088
00089
00090
00091
00092 #define PAP_WITHPEER 1
00093 #define PAP_PEER 2
00094 #define CHAP_WITHPEER 4
00095 #define CHAP_PEER 8
00096
00097
00098
00099
00100
00101
00102 struct wordlist {
00103 struct wordlist *next;
00104 char word[1];
00105 };
00106
00107
00108
00109
00110
00111 extern char *crypt (const char *, const char *);
00112
00113
00114
00115 static void network_phase (int);
00116 static void check_idle (void *);
00117 static void connect_time_expired (void *);
00118 #if 0
00119 static int login (char *, char *, char **, int *);
00120 #endif
00121 static void logout (void);
00122 static int null_login (int);
00123 static int get_pap_passwd (int, char *, char *);
00124 static int have_pap_secret (void);
00125 static int have_chap_secret (char *, char *, u32_t);
00126 static int ip_addr_check (u32_t, struct wordlist *);
00127 #if 0
00128 static void set_allowed_addrs(int unit, struct wordlist *addrs);
00129 static void free_wordlist (struct wordlist *);
00130 #endif
00131 #if CBCP_SUPPORT
00132 static void callback_phase (int);
00133 #endif
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144 #if PAP_SUPPORT || CHAP_SUPPORT
00145
00146 static char peer_authname[MAXNAMELEN];
00147 #endif
00148
00149
00150 static int auth_pending[NUM_PPP];
00151
00152
00153 static int logged_in;
00154
00155
00156 static int did_authup;
00157
00158
00159 static struct wordlist *addresses[NUM_PPP];
00160
00161
00162 static int num_np_open;
00163
00164
00165 static int num_np_up;
00166
00167 #if PAP_SUPPORT || CHAP_SUPPORT
00168
00169 static int passwd_from_file;
00170 #endif
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180 void
00181 link_required(int unit)
00182 {
00183 LWIP_UNUSED_ARG(unit);
00184
00185 AUTHDEBUG((LOG_INFO, "link_required: %d\n", unit));
00186 }
00187
00188
00189
00190
00191
00192 void
00193 link_terminated(int unit)
00194 {
00195 AUTHDEBUG((LOG_INFO, "link_terminated: %d\n", unit));
00196 if (lcp_phase[unit] == PHASE_DEAD) {
00197 return;
00198 }
00199 if (logged_in) {
00200 logout();
00201 }
00202 lcp_phase[unit] = PHASE_DEAD;
00203 AUTHDEBUG((LOG_NOTICE, "Connection terminated.\n"));
00204 pppLinkTerminated(unit);
00205 }
00206
00207
00208
00209
00210 void
00211 link_down(int unit)
00212 {
00213 int i;
00214 struct protent *protp;
00215
00216 AUTHDEBUG((LOG_INFO, "link_down: %d\n", unit));
00217 if (did_authup) {
00218
00219 did_authup = 0;
00220 }
00221 for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
00222 if (!protp->enabled_flag) {
00223 continue;
00224 }
00225 if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) {
00226 (*protp->lowerdown)(unit);
00227 }
00228 if (protp->protocol < 0xC000 && protp->close != NULL) {
00229 (*protp->close)(unit, "LCP down");
00230 }
00231 }
00232 num_np_open = 0;
00233 num_np_up = 0;
00234 if (lcp_phase[unit] != PHASE_DEAD) {
00235 lcp_phase[unit] = PHASE_TERMINATE;
00236 }
00237 pppLinkDown(unit);
00238 }
00239
00240
00241
00242
00243
00244 void
00245 link_established(int unit)
00246 {
00247 int auth;
00248 int i;
00249 struct protent *protp;
00250 lcp_options *wo = &lcp_wantoptions[unit];
00251 lcp_options *go = &lcp_gotoptions[unit];
00252 #if PAP_SUPPORT || CHAP_SUPPORT
00253 lcp_options *ho = &lcp_hisoptions[unit];
00254 #endif
00255
00256 AUTHDEBUG((LOG_INFO, "link_established: %d\n", unit));
00257
00258
00259
00260 for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
00261 if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) {
00262 (*protp->lowerup)(unit);
00263 }
00264 }
00265 if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) {
00266
00267
00268
00269
00270
00271 if (!wo->neg_upap || !null_login(unit)) {
00272 AUTHDEBUG((LOG_WARNING, "peer refused to authenticate\n"));
00273 lcp_close(unit, "peer refused to authenticate");
00274 return;
00275 }
00276 }
00277
00278 lcp_phase[unit] = PHASE_AUTHENTICATE;
00279 auth = 0;
00280 #if CHAP_SUPPORT
00281 if (go->neg_chap) {
00282 ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype);
00283 auth |= CHAP_PEER;
00284 }
00285 #endif
00286 #if PAP_SUPPORT && CHAP_SUPPORT
00287 else
00288 #endif
00289 #if PAP_SUPPORT
00290 if (go->neg_upap) {
00291 upap_authpeer(unit);
00292 auth |= PAP_PEER;
00293 }
00294 #endif
00295 #if CHAP_SUPPORT
00296 if (ho->neg_chap) {
00297 ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype);
00298 auth |= CHAP_WITHPEER;
00299 }
00300 #endif
00301 #if PAP_SUPPORT && CHAP_SUPPORT
00302 else
00303 #endif
00304 #if PAP_SUPPORT
00305 if (ho->neg_upap) {
00306 if (ppp_settings.passwd[0] == 0) {
00307 passwd_from_file = 1;
00308 if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) {
00309 AUTHDEBUG((LOG_ERR, "No secret found for PAP login\n"));
00310 }
00311 }
00312 upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd);
00313 auth |= PAP_WITHPEER;
00314 }
00315 #endif
00316 auth_pending[unit] = auth;
00317
00318 if (!auth) {
00319 network_phase(unit);
00320 }
00321 }
00322
00323
00324
00325
00326 void
00327 auth_peer_fail(int unit, u16_t protocol)
00328 {
00329 LWIP_UNUSED_ARG(protocol);
00330
00331 AUTHDEBUG((LOG_INFO, "auth_peer_fail: %d proto=%X\n", unit, protocol));
00332
00333
00334
00335 lcp_close(unit, "Authentication failed");
00336 }
00337
00338
00339 #if PAP_SUPPORT || CHAP_SUPPORT
00340
00341
00342
00343 void
00344 auth_peer_success(int unit, u16_t protocol, char *name, int namelen)
00345 {
00346 int pbit;
00347
00348 AUTHDEBUG((LOG_INFO, "auth_peer_success: %d proto=%X\n", unit, protocol));
00349 switch (protocol) {
00350 case PPP_CHAP:
00351 pbit = CHAP_PEER;
00352 break;
00353 case PPP_PAP:
00354 pbit = PAP_PEER;
00355 break;
00356 default:
00357 AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", protocol));
00358 return;
00359 }
00360
00361
00362
00363
00364 if (namelen > sizeof(peer_authname) - 1) {
00365 namelen = sizeof(peer_authname) - 1;
00366 }
00367 BCOPY(name, peer_authname, namelen);
00368 peer_authname[namelen] = 0;
00369
00370
00371
00372
00373
00374 if ((auth_pending[unit] &= ~pbit) == 0) {
00375 network_phase(unit);
00376 }
00377 }
00378
00379
00380
00381
00382 void
00383 auth_withpeer_fail(int unit, u16_t protocol)
00384 {
00385 int errCode = PPPERR_AUTHFAIL;
00386
00387 LWIP_UNUSED_ARG(protocol);
00388
00389 AUTHDEBUG((LOG_INFO, "auth_withpeer_fail: %d proto=%X\n", unit, protocol));
00390 if (passwd_from_file) {
00391 BZERO(ppp_settings.passwd, MAXSECRETLEN);
00392 }
00393
00394
00395
00396
00397
00398 pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode);
00399
00400
00401
00402
00403
00404
00405 }
00406
00407
00408
00409
00410 void
00411 auth_withpeer_success(int unit, u16_t protocol)
00412 {
00413 int pbit;
00414
00415 AUTHDEBUG((LOG_INFO, "auth_withpeer_success: %d proto=%X\n", unit, protocol));
00416 switch (protocol) {
00417 case PPP_CHAP:
00418 pbit = CHAP_WITHPEER;
00419 break;
00420 case PPP_PAP:
00421 if (passwd_from_file) {
00422 BZERO(ppp_settings.passwd, MAXSECRETLEN);
00423 }
00424 pbit = PAP_WITHPEER;
00425 break;
00426 default:
00427 AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", protocol));
00428 pbit = 0;
00429 }
00430
00431
00432
00433
00434
00435 if ((auth_pending[unit] &= ~pbit) == 0) {
00436 network_phase(unit);
00437 }
00438 }
00439 #endif
00440
00441
00442
00443
00444
00445 void
00446 np_up(int unit, u16_t proto)
00447 {
00448 LWIP_UNUSED_ARG(unit);
00449 LWIP_UNUSED_ARG(proto);
00450
00451 AUTHDEBUG((LOG_INFO, "np_up: %d proto=%X\n", unit, proto));
00452 if (num_np_up == 0) {
00453 AUTHDEBUG((LOG_INFO, "np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit));
00454
00455
00456
00457 if (ppp_settings.idle_time_limit > 0) {
00458 TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit);
00459 }
00460
00461
00462
00463
00464
00465 if (ppp_settings.maxconnect > 0) {
00466 TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect);
00467 }
00468 }
00469 ++num_np_up;
00470 }
00471
00472
00473
00474
00475 void
00476 np_down(int unit, u16_t proto)
00477 {
00478 LWIP_UNUSED_ARG(unit);
00479 LWIP_UNUSED_ARG(proto);
00480
00481 AUTHDEBUG((LOG_INFO, "np_down: %d proto=%X\n", unit, proto));
00482 if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) {
00483 UNTIMEOUT(check_idle, NULL);
00484 }
00485 }
00486
00487
00488
00489
00490 void
00491 np_finished(int unit, u16_t proto)
00492 {
00493 LWIP_UNUSED_ARG(unit);
00494 LWIP_UNUSED_ARG(proto);
00495
00496 AUTHDEBUG((LOG_INFO, "np_finished: %d proto=%X\n", unit, proto));
00497 if (--num_np_open <= 0) {
00498
00499 lcp_close(0, "No network protocols running");
00500 }
00501 }
00502
00503
00504
00505
00506
00507
00508 void
00509 auth_reset(int unit)
00510 {
00511 lcp_options *go = &lcp_gotoptions[unit];
00512 lcp_options *ao = &lcp_allowoptions[0];
00513 ipcp_options *ipwo = &ipcp_wantoptions[0];
00514 u32_t remote;
00515
00516 AUTHDEBUG((LOG_INFO, "auth_reset: %d\n", unit));
00517 ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL));
00518 ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 ;
00519
00520 if (go->neg_upap && !have_pap_secret()) {
00521 go->neg_upap = 0;
00522 }
00523 if (go->neg_chap) {
00524 remote = ipwo->accept_remote? 0: ipwo->hisaddr;
00525 if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) {
00526 go->neg_chap = 0;
00527 }
00528 }
00529 }
00530
00531 #if PAP_SUPPORT
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542 int
00543 check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen)
00544 {
00545 #if 1
00546 LWIP_UNUSED_ARG(unit);
00547 LWIP_UNUSED_ARG(auser);
00548 LWIP_UNUSED_ARG(userlen);
00549 LWIP_UNUSED_ARG(apasswd);
00550 LWIP_UNUSED_ARG(passwdlen);
00551 LWIP_UNUSED_ARG(msglen);
00552 *msg = (char *) 0;
00553 return UPAP_AUTHACK;
00554 #else
00555 int ret = 0;
00556 struct wordlist *addrs = NULL;
00557 char passwd[256], user[256];
00558 char secret[MAXWORDLEN];
00559 static u_short attempts = 0;
00560
00561
00562
00563
00564 BCOPY(apasswd, passwd, passwdlen);
00565 passwd[passwdlen] = '\0';
00566 BCOPY(auser, user, userlen);
00567 user[userlen] = '\0';
00568 *msg = (char *) 0;
00569
00570
00571 ret = UPAP_AUTHACK;
00572
00573 if (ret == UPAP_AUTHNAK) {
00574 if (*msg == (char *) 0) {
00575 *msg = "Login incorrect";
00576 }
00577 *msglen = strlen(*msg);
00578
00579
00580
00581
00582
00583 if (attempts++ >= 10) {
00584 AUTHDEBUG((LOG_WARNING, "%d LOGIN FAILURES BY %s\n", attempts, user));
00585
00586 }
00587 if (attempts > 3) {
00588 sys_msleep((attempts - 3) * 5);
00589 }
00590 if (addrs != NULL) {
00591 free_wordlist(addrs);
00592 }
00593 } else {
00594 attempts = 0;
00595 if (*msg == (char *) 0) {
00596 *msg = "Login ok";
00597 }
00598 *msglen = strlen(*msg);
00599 set_allowed_addrs(unit, addrs);
00600 }
00601
00602 BZERO(passwd, sizeof(passwd));
00603 BZERO(secret, sizeof(secret));
00604
00605 return ret;
00606 #endif
00607 }
00608 #endif
00609
00610
00611
00612
00613
00614
00615 int
00616 auth_ip_addr(int unit, u32_t addr)
00617 {
00618 return ip_addr_check(addr, addresses[unit]);
00619 }
00620
00621
00622
00623
00624
00625
00626 int
00627 bad_ip_adrs(u32_t addr)
00628 {
00629 addr = ntohl(addr);
00630 return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
00631 || IN_MULTICAST(addr) || IN_BADCLASS(addr);
00632 }
00633
00634
00635 #if CHAP_SUPPORT
00636
00637
00638
00639
00640
00641 int get_secret( int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs)
00642 {
00643 #if 1
00644 int len;
00645 struct wordlist *addrs;
00646
00647 LWIP_UNUSED_ARG(unit);
00648 LWIP_UNUSED_ARG(server);
00649 LWIP_UNUSED_ARG(save_addrs);
00650
00651 addrs = NULL;
00652
00653 if(!client || !client[0] || strcmp(client, ppp_settings.user)) {
00654 return 0;
00655 }
00656
00657 len = strlen(ppp_settings.passwd);
00658 if (len > MAXSECRETLEN) {
00659 AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server));
00660 len = MAXSECRETLEN;
00661 }
00662
00663 BCOPY(ppp_settings.passwd, secret, len);
00664 *secret_len = len;
00665
00666 return 1;
00667 #else
00668 int ret = 0, len;
00669 struct wordlist *addrs;
00670 char secbuf[MAXWORDLEN];
00671
00672 addrs = NULL;
00673 secbuf[0] = 0;
00674
00675
00676 if (ret < 0) {
00677 return 0;
00678 }
00679
00680 if (save_addrs) {
00681 set_allowed_addrs(unit, addrs);
00682 }
00683
00684 len = strlen(secbuf);
00685 if (len > MAXSECRETLEN) {
00686 AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server));
00687 len = MAXSECRETLEN;
00688 }
00689
00690 BCOPY(secbuf, secret, len);
00691 BZERO(secbuf, sizeof(secbuf));
00692 *secret_len = len;
00693
00694 return 1;
00695 #endif
00696 }
00697 #endif
00698
00699
00700 #if 0
00701
00702
00703
00704 void
00705 auth_check_options(void)
00706 {
00707 lcp_options *wo = &lcp_wantoptions[0];
00708 int can_auth;
00709 ipcp_options *ipwo = &ipcp_wantoptions[0];
00710 u32_t remote;
00711
00712
00713 if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) {
00714 strcpy(ppp_settings.our_name, ppp_settings.hostname);
00715 }
00716
00717 if (ppp_settings.user[0] == 0) {
00718 strcpy(ppp_settings.user, ppp_settings.our_name);
00719 }
00720
00721
00722 if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) {
00723 wo->neg_chap = 1;
00724 wo->neg_upap = 1;
00725 }
00726
00727
00728
00729
00730
00731 can_auth = wo->neg_upap && have_pap_secret();
00732 if (!can_auth && wo->neg_chap) {
00733 remote = ipwo->accept_remote? 0: ipwo->hisaddr;
00734 can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote);
00735 }
00736
00737 if (ppp_settings.auth_required && !can_auth) {
00738 ppp_panic("No auth secret");
00739 }
00740 }
00741 #endif
00742
00743
00744
00745
00746
00747
00748
00749
00750 static void
00751 network_phase(int unit)
00752 {
00753 int i;
00754 struct protent *protp;
00755 lcp_options *go = &lcp_gotoptions[unit];
00756
00757
00758
00759
00760 if ((go->neg_chap || go->neg_upap) && !did_authup) {
00761
00762 did_authup = 1;
00763 }
00764
00765 #if CBCP_SUPPORT
00766
00767
00768
00769 if (go->neg_cbcp) {
00770 lcp_phase[unit] = PHASE_CALLBACK;
00771 (*cbcp_protent.open)(unit);
00772 return;
00773 }
00774 #endif
00775
00776 lcp_phase[unit] = PHASE_NETWORK;
00777 for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
00778 if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) {
00779 (*protp->open)(unit);
00780 if (protp->protocol != PPP_CCP) {
00781 ++num_np_open;
00782 }
00783 }
00784 }
00785
00786 if (num_np_open == 0) {
00787
00788 lcp_close(0, "No network protocols running");
00789 }
00790 }
00791
00792
00793
00794
00795
00796 static void
00797 check_idle(void *arg)
00798 {
00799 struct ppp_idle idle;
00800 u_short itime;
00801
00802 LWIP_UNUSED_ARG(arg);
00803 if (!get_idle_time(0, &idle)) {
00804 return;
00805 }
00806 itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle);
00807 if (itime >= ppp_settings.idle_time_limit) {
00808
00809 AUTHDEBUG((LOG_INFO, "Terminating connection due to lack of activity.\n"));
00810 lcp_close(0, "Link inactive");
00811 } else {
00812 TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime);
00813 }
00814 }
00815
00816
00817
00818
00819 static void
00820 connect_time_expired(void *arg)
00821 {
00822 LWIP_UNUSED_ARG(arg);
00823
00824 AUTHDEBUG((LOG_INFO, "Connect time expired\n"));
00825 lcp_close(0, "Connect time expired");
00826 }
00827
00828 #if 0
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838 static int
00839 login(char *user, char *passwd, char **msg, int *msglen)
00840 {
00841
00842 return (UPAP_AUTHNAK);
00843 }
00844 #endif
00845
00846
00847
00848
00849 static void
00850 logout(void)
00851 {
00852 logged_in = 0;
00853 }
00854
00855
00856
00857
00858
00859
00860 static int
00861 null_login(int unit)
00862 {
00863 LWIP_UNUSED_ARG(unit);
00864
00865 return 0;
00866 }
00867
00868
00869
00870
00871
00872
00873 static int
00874 get_pap_passwd(int unit, char *user, char *passwd)
00875 {
00876 LWIP_UNUSED_ARG(unit);
00877
00878
00879
00880
00881
00882 if(user) {
00883 strcpy(user, "none");
00884 }
00885 if(passwd) {
00886 strcpy(passwd, "none");
00887 }
00888 return 1;
00889 }
00890
00891
00892
00893
00894
00895 static int
00896 have_pap_secret(void)
00897 {
00898
00899 return 0;
00900 }
00901
00902
00903
00904
00905
00906
00907
00908 static int
00909 have_chap_secret(char *client, char *server, u32_t remote)
00910 {
00911 LWIP_UNUSED_ARG(client);
00912 LWIP_UNUSED_ARG(server);
00913 LWIP_UNUSED_ARG(remote);
00914
00915 return 0;
00916 }
00917
00918 #if 0
00919
00920
00921
00922 static void
00923 set_allowed_addrs(int unit, struct wordlist *addrs)
00924 {
00925 if (addresses[unit] != NULL) {
00926 free_wordlist(addresses[unit]);
00927 }
00928 addresses[unit] = addrs;
00929
00930 #if 0
00931
00932
00933
00934
00935 if (addrs != NULL && addrs->next == NULL) {
00936 char *p = addrs->word;
00937 struct ipcp_options *wo = &ipcp_wantoptions[unit];
00938 u32_t a;
00939 struct hostent *hp;
00940
00941 if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) {
00942 hp = gethostbyname(p);
00943 if (hp != NULL && hp->h_addrtype == AF_INET) {
00944 a = *(u32_t *)hp->h_addr;
00945 } else {
00946 a = inet_addr(p);
00947 }
00948 if (a != (u32_t) -1) {
00949 wo->hisaddr = a;
00950 }
00951 }
00952 }
00953 #endif
00954 }
00955 #endif
00956
00957 static int
00958 ip_addr_check(u32_t addr, struct wordlist *addrs)
00959 {
00960
00961 if (bad_ip_adrs(addr)) {
00962 return 0;
00963 }
00964
00965 if (addrs == NULL) {
00966 return !ppp_settings.auth_required;
00967 }
00968
00969
00970 return 1;
00971 }
00972
00973 #if 0
00974
00975
00976
00977 static void
00978 free_wordlist(struct wordlist *wp)
00979 {
00980 struct wordlist *next;
00981
00982 while (wp != NULL) {
00983 next = wp->next;
00984 free(wp);
00985 wp = next;
00986 }
00987 }
00988 #endif
00989
00990 #endif