Netservices modded to read fragmented HTTP respsonse/payload from special purpose server - 180 bytes only
Embed:
(wiki syntax)
Show/hide line numbers
pap.c
00001 /***************************************************************************** 00002 * pap.c - Network Password Authentication Protocol program file. 00003 * 00004 * Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. 00005 * portions Copyright (c) 1997 by Global Election Systems Inc. 00006 * 00007 * The authors hereby grant permission to use, copy, modify, distribute, 00008 * and license this software and its documentation for any purpose, provided 00009 * that existing copyright notices are retained in all copies and that this 00010 * notice and the following disclaimer are included verbatim in any 00011 * distributions. No written agreement, license, or royalty fee is required 00012 * for any of the authorized uses. 00013 * 00014 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR 00015 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00016 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 00017 * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00018 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 00019 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00020 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00021 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00022 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 00023 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00024 * 00025 ****************************************************************************** 00026 * REVISION HISTORY 00027 * 00028 * 03-01-01 Marc Boucher <marc@mbsi.ca> 00029 * Ported to lwIP. 00030 * 97-12-12 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc. 00031 * Original. 00032 *****************************************************************************/ 00033 /* 00034 * upap.c - User/Password Authentication Protocol. 00035 * 00036 * Copyright (c) 1989 Carnegie Mellon University. 00037 * All rights reserved. 00038 * 00039 * Redistribution and use in source and binary forms are permitted 00040 * provided that the above copyright notice and this paragraph are 00041 * duplicated in all such forms and that any documentation, 00042 * advertising materials, and other materials related to such 00043 * distribution and use acknowledge that the software was developed 00044 * by Carnegie Mellon University. The name of the 00045 * University may not be used to endorse or promote products derived 00046 * from this software without specific prior written permission. 00047 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 00048 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 00049 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 00050 */ 00051 00052 #include "lwip/opt.h" 00053 00054 #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ 00055 00056 #if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ 00057 00058 #include "ppp.h" 00059 #include "pppdebug.h" 00060 00061 #include "auth.h" 00062 #include "pap.h" 00063 00064 #include <string.h> 00065 00066 #if 0 /* UNUSED */ 00067 static bool hide_password = 1; 00068 00069 /* 00070 * Command-line options. 00071 */ 00072 static option_t pap_option_list[] = { 00073 { "hide-password", o_bool, &hide_password, 00074 "Don't output passwords to log", 1 }, 00075 { "show-password", o_bool, &hide_password, 00076 "Show password string in debug log messages", 0 }, 00077 { "pap-restart", o_int, &upap[0].us_timeouttime, 00078 "Set retransmit timeout for PAP" }, 00079 { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, 00080 "Set max number of transmissions for auth-reqs" }, 00081 { "pap-timeout", o_int, &upap[0].us_reqtimeout, 00082 "Set time limit for peer PAP authentication" }, 00083 { NULL } 00084 }; 00085 #endif 00086 00087 /* 00088 * Protocol entry points. 00089 */ 00090 static void upap_init (int); 00091 static void upap_lowerup (int); 00092 static void upap_lowerdown (int); 00093 static void upap_input (int, u_char *, int); 00094 static void upap_protrej (int); 00095 #if PPP_ADDITIONAL_CALLBACKS 00096 static int upap_printpkt (u_char *, int, void (*)(void *, char *, ...), void *); 00097 #endif /* PPP_ADDITIONAL_CALLBACKS */ 00098 00099 struct protent pap_protent = { 00100 PPP_PAP, 00101 upap_init, 00102 upap_input, 00103 upap_protrej, 00104 upap_lowerup, 00105 upap_lowerdown, 00106 NULL, 00107 NULL, 00108 #if PPP_ADDITIONAL_CALLBACKS 00109 upap_printpkt, 00110 NULL, 00111 #endif /* PPP_ADDITIONAL_CALLBACKS */ 00112 1, 00113 "PAP", 00114 #if PPP_ADDITIONAL_CALLBACKS 00115 NULL, 00116 NULL, 00117 NULL 00118 #endif /* PPP_ADDITIONAL_CALLBACKS */ 00119 }; 00120 00121 upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ 00122 00123 static void upap_timeout (void *); 00124 static void upap_reqtimeout(void *); 00125 static void upap_rauthreq (upap_state *, u_char *, u_char, int); 00126 static void upap_rauthack (upap_state *, u_char *, int, int); 00127 static void upap_rauthnak (upap_state *, u_char *, int, int); 00128 static void upap_sauthreq (upap_state *); 00129 static void upap_sresp (upap_state *, u_char, u_char, char *, int); 00130 00131 00132 /* 00133 * upap_init - Initialize a UPAP unit. 00134 */ 00135 static void 00136 upap_init(int unit) 00137 { 00138 upap_state *u = &upap[unit]; 00139 00140 UPAPDEBUG(LOG_INFO, ("upap_init: %d\n", unit)); 00141 u->us_unit = unit; 00142 u->us_user = NULL; 00143 u->us_userlen = 0; 00144 u->us_passwd = NULL; 00145 u->us_passwdlen = 0; 00146 u->us_clientstate = UPAPCS_INITIAL; 00147 u->us_serverstate = UPAPSS_INITIAL; 00148 u->us_id = 0; 00149 u->us_timeouttime = UPAP_DEFTIMEOUT; 00150 u->us_maxtransmits = 10; 00151 u->us_reqtimeout = UPAP_DEFREQTIME; 00152 } 00153 00154 /* 00155 * upap_authwithpeer - Authenticate us with our peer (start client). 00156 * 00157 * Set new state and send authenticate's. 00158 */ 00159 void 00160 upap_authwithpeer(int unit, char *user, char *password) 00161 { 00162 upap_state *u = &upap[unit]; 00163 00164 UPAPDEBUG(LOG_INFO, ("upap_authwithpeer: %d user=%s password=%s s=%d\n", 00165 unit, user, password, u->us_clientstate)); 00166 00167 /* Save the username and password we're given */ 00168 u->us_user = user; 00169 u->us_userlen = (int)strlen(user); 00170 u->us_passwd = password; 00171 u->us_passwdlen = (int)strlen(password); 00172 00173 u->us_transmits = 0; 00174 00175 /* Lower layer up yet? */ 00176 if (u->us_clientstate == UPAPCS_INITIAL || 00177 u->us_clientstate == UPAPCS_PENDING) { 00178 u->us_clientstate = UPAPCS_PENDING; 00179 return; 00180 } 00181 00182 upap_sauthreq(u); /* Start protocol */ 00183 } 00184 00185 00186 /* 00187 * upap_authpeer - Authenticate our peer (start server). 00188 * 00189 * Set new state. 00190 */ 00191 void 00192 upap_authpeer(int unit) 00193 { 00194 upap_state *u = &upap[unit]; 00195 00196 /* Lower layer up yet? */ 00197 if (u->us_serverstate == UPAPSS_INITIAL || 00198 u->us_serverstate == UPAPSS_PENDING) { 00199 u->us_serverstate = UPAPSS_PENDING; 00200 return; 00201 } 00202 00203 u->us_serverstate = UPAPSS_LISTEN; 00204 if (u->us_reqtimeout > 0) { 00205 TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); 00206 } 00207 } 00208 00209 /* 00210 * upap_timeout - Retransmission timer for sending auth-reqs expired. 00211 */ 00212 static void 00213 upap_timeout(void *arg) 00214 { 00215 upap_state *u = (upap_state *) arg; 00216 00217 UPAPDEBUG(LOG_INFO, ("upap_timeout: %d timeout %d expired s=%d\n", 00218 u->us_unit, u->us_timeouttime, u->us_clientstate)); 00219 00220 if (u->us_clientstate != UPAPCS_AUTHREQ) { 00221 UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n")); 00222 return; 00223 } 00224 00225 if (u->us_transmits >= u->us_maxtransmits) { 00226 /* give up in disgust */ 00227 UPAPDEBUG(LOG_ERR, ("No response to PAP authenticate-requests\n")); 00228 u->us_clientstate = UPAPCS_BADAUTH; 00229 auth_withpeer_fail(u->us_unit, PPP_PAP); 00230 return; 00231 } 00232 00233 upap_sauthreq(u); /* Send Authenticate-Request and set upap timeout*/ 00234 } 00235 00236 00237 /* 00238 * upap_reqtimeout - Give up waiting for the peer to send an auth-req. 00239 */ 00240 static void 00241 upap_reqtimeout(void *arg) 00242 { 00243 upap_state *u = (upap_state *) arg; 00244 00245 if (u->us_serverstate != UPAPSS_LISTEN) { 00246 return; /* huh?? */ 00247 } 00248 00249 auth_peer_fail(u->us_unit, PPP_PAP); 00250 u->us_serverstate = UPAPSS_BADAUTH; 00251 } 00252 00253 00254 /* 00255 * upap_lowerup - The lower layer is up. 00256 * 00257 * Start authenticating if pending. 00258 */ 00259 static void 00260 upap_lowerup(int unit) 00261 { 00262 upap_state *u = &upap[unit]; 00263 00264 UPAPDEBUG(LOG_INFO, ("upap_lowerup: init %d clientstate s=%d\n", unit, u->us_clientstate)); 00265 00266 if (u->us_clientstate == UPAPCS_INITIAL) { 00267 u->us_clientstate = UPAPCS_CLOSED; 00268 } else if (u->us_clientstate == UPAPCS_PENDING) { 00269 upap_sauthreq(u); /* send an auth-request */ 00270 /* now client state is UPAPCS__AUTHREQ */ 00271 } 00272 00273 if (u->us_serverstate == UPAPSS_INITIAL) { 00274 u->us_serverstate = UPAPSS_CLOSED; 00275 } else if (u->us_serverstate == UPAPSS_PENDING) { 00276 u->us_serverstate = UPAPSS_LISTEN; 00277 if (u->us_reqtimeout > 0) { 00278 TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); 00279 } 00280 } 00281 } 00282 00283 00284 /* 00285 * upap_lowerdown - The lower layer is down. 00286 * 00287 * Cancel all timeouts. 00288 */ 00289 static void 00290 upap_lowerdown(int unit) 00291 { 00292 upap_state *u = &upap[unit]; 00293 00294 UPAPDEBUG(LOG_INFO, ("upap_lowerdown: %d s=%d\n", unit, u->us_clientstate)); 00295 00296 if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */ 00297 UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ 00298 } 00299 if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) { 00300 UNTIMEOUT(upap_reqtimeout, u); 00301 } 00302 00303 u->us_clientstate = UPAPCS_INITIAL; 00304 u->us_serverstate = UPAPSS_INITIAL; 00305 } 00306 00307 00308 /* 00309 * upap_protrej - Peer doesn't speak this protocol. 00310 * 00311 * This shouldn't happen. In any case, pretend lower layer went down. 00312 */ 00313 static void 00314 upap_protrej(int unit) 00315 { 00316 upap_state *u = &upap[unit]; 00317 00318 if (u->us_clientstate == UPAPCS_AUTHREQ) { 00319 UPAPDEBUG(LOG_ERR, ("PAP authentication failed due to protocol-reject\n")); 00320 auth_withpeer_fail(unit, PPP_PAP); 00321 } 00322 if (u->us_serverstate == UPAPSS_LISTEN) { 00323 UPAPDEBUG(LOG_ERR, ("PAP authentication of peer failed (protocol-reject)\n")); 00324 auth_peer_fail(unit, PPP_PAP); 00325 } 00326 upap_lowerdown(unit); 00327 } 00328 00329 00330 /* 00331 * upap_input - Input UPAP packet. 00332 */ 00333 static void 00334 upap_input(int unit, u_char *inpacket, int l) 00335 { 00336 upap_state *u = &upap[unit]; 00337 u_char *inp; 00338 u_char code, id; 00339 int len; 00340 00341 /* 00342 * Parse header (code, id and length). 00343 * If packet too short, drop it. 00344 */ 00345 inp = inpacket; 00346 if (l < (int)UPAP_HEADERLEN) { 00347 UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short header.\n")); 00348 return; 00349 } 00350 GETCHAR(code, inp); 00351 GETCHAR(id, inp); 00352 GETSHORT(len, inp); 00353 if (len < (int)UPAP_HEADERLEN) { 00354 UPAPDEBUG(LOG_INFO, ("pap_input: rcvd illegal length.\n")); 00355 return; 00356 } 00357 if (len > l) { 00358 UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short packet.\n")); 00359 return; 00360 } 00361 len -= UPAP_HEADERLEN; 00362 00363 /* 00364 * Action depends on code. 00365 */ 00366 switch (code) { 00367 case UPAP_AUTHREQ: 00368 upap_rauthreq(u, inp, id, len); 00369 break; 00370 00371 case UPAP_AUTHACK: 00372 upap_rauthack(u, inp, id, len); 00373 break; 00374 00375 case UPAP_AUTHNAK: 00376 upap_rauthnak(u, inp, id, len); 00377 break; 00378 00379 default: /* XXX Need code reject */ 00380 UPAPDEBUG(LOG_INFO, ("pap_input: UNHANDLED default: code: %d, id: %d, len: %d.\n", code, id, len)); 00381 break; 00382 } 00383 } 00384 00385 00386 /* 00387 * upap_rauth - Receive Authenticate. 00388 */ 00389 static void 00390 upap_rauthreq(upap_state *u, u_char *inp, u_char id, int len) 00391 { 00392 u_char ruserlen, rpasswdlen; 00393 char *ruser, *rpasswd; 00394 u_char retcode; 00395 char *msg; 00396 int msglen; 00397 00398 UPAPDEBUG(LOG_INFO, ("pap_rauth: Rcvd id %d.\n", id)); 00399 00400 if (u->us_serverstate < UPAPSS_LISTEN) { 00401 return; 00402 } 00403 00404 /* 00405 * If we receive a duplicate authenticate-request, we are 00406 * supposed to return the same status as for the first request. 00407 */ 00408 if (u->us_serverstate == UPAPSS_OPEN) { 00409 upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ 00410 return; 00411 } 00412 if (u->us_serverstate == UPAPSS_BADAUTH) { 00413 upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ 00414 return; 00415 } 00416 00417 /* 00418 * Parse user/passwd. 00419 */ 00420 if (len < (int)sizeof (u_char)) { 00421 UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); 00422 return; 00423 } 00424 GETCHAR(ruserlen, inp); 00425 len -= sizeof (u_char) + ruserlen + sizeof (u_char); 00426 if (len < 0) { 00427 UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); 00428 return; 00429 } 00430 ruser = (char *) inp; 00431 INCPTR(ruserlen, inp); 00432 GETCHAR(rpasswdlen, inp); 00433 if (len < rpasswdlen) { 00434 UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); 00435 return; 00436 } 00437 rpasswd = (char *) inp; 00438 00439 /* 00440 * Check the username and password given. 00441 */ 00442 retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen); 00443 /* lwip: currently retcode is always UPAP_AUTHACK */ 00444 BZERO(rpasswd, rpasswdlen); 00445 00446 upap_sresp(u, retcode, id, msg, msglen); 00447 00448 if (retcode == UPAP_AUTHACK) { 00449 u->us_serverstate = UPAPSS_OPEN; 00450 auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); 00451 } else { 00452 u->us_serverstate = UPAPSS_BADAUTH; 00453 auth_peer_fail(u->us_unit, PPP_PAP); 00454 } 00455 00456 if (u->us_reqtimeout > 0) { 00457 UNTIMEOUT(upap_reqtimeout, u); 00458 } 00459 } 00460 00461 00462 /* 00463 * upap_rauthack - Receive Authenticate-Ack. 00464 */ 00465 static void 00466 upap_rauthack(upap_state *u, u_char *inp, int id, int len) 00467 { 00468 u_char msglen; 00469 char *msg; 00470 00471 LWIP_UNUSED_ARG(id); 00472 00473 UPAPDEBUG(LOG_INFO, ("pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate)); 00474 00475 if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ 00476 UPAPDEBUG(LOG_INFO, ("pap_rauthack: us_clientstate != UPAPCS_AUTHREQ\n")); 00477 return; 00478 } 00479 00480 /* 00481 * Parse message. 00482 */ 00483 if (len < (int)sizeof (u_char)) { 00484 UPAPDEBUG(LOG_INFO, ("pap_rauthack: ignoring missing msg-length.\n")); 00485 } else { 00486 GETCHAR(msglen, inp); 00487 if (msglen > 0) { 00488 len -= sizeof (u_char); 00489 if (len < msglen) { 00490 UPAPDEBUG(LOG_INFO, ("pap_rauthack: rcvd short packet.\n")); 00491 return; 00492 } 00493 msg = (char *) inp; 00494 PRINTMSG(msg, msglen); 00495 } 00496 } 00497 UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ 00498 u->us_clientstate = UPAPCS_OPEN; 00499 00500 auth_withpeer_success(u->us_unit, PPP_PAP); 00501 } 00502 00503 00504 /* 00505 * upap_rauthnak - Receive Authenticate-Nak. 00506 */ 00507 static void 00508 upap_rauthnak(upap_state *u, u_char *inp, int id, int len) 00509 { 00510 u_char msglen; 00511 char *msg; 00512 00513 LWIP_UNUSED_ARG(id); 00514 00515 UPAPDEBUG(LOG_INFO, ("pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate)); 00516 00517 if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ 00518 return; 00519 } 00520 00521 /* 00522 * Parse message. 00523 */ 00524 if (len < sizeof (u_char)) { 00525 UPAPDEBUG(LOG_INFO, ("pap_rauthnak: ignoring missing msg-length.\n")); 00526 } else { 00527 GETCHAR(msglen, inp); 00528 if(msglen > 0) { 00529 len -= sizeof (u_char); 00530 if (len < msglen) { 00531 UPAPDEBUG(LOG_INFO, ("pap_rauthnak: rcvd short packet.\n")); 00532 return; 00533 } 00534 msg = (char *) inp; 00535 PRINTMSG(msg, msglen); 00536 } 00537 } 00538 00539 u->us_clientstate = UPAPCS_BADAUTH; 00540 00541 UPAPDEBUG(LOG_ERR, ("PAP authentication failed\n")); 00542 auth_withpeer_fail(u->us_unit, PPP_PAP); 00543 } 00544 00545 00546 /* 00547 * upap_sauthreq - Send an Authenticate-Request. 00548 */ 00549 static void 00550 upap_sauthreq(upap_state *u) 00551 { 00552 u_char *outp; 00553 int outlen; 00554 00555 outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) 00556 + u->us_userlen + u->us_passwdlen; 00557 outp = outpacket_buf[u->us_unit]; 00558 00559 MAKEHEADER(outp, PPP_PAP); 00560 00561 PUTCHAR(UPAP_AUTHREQ, outp); 00562 PUTCHAR(++u->us_id, outp); 00563 PUTSHORT(outlen, outp); 00564 PUTCHAR(u->us_userlen, outp); 00565 BCOPY(u->us_user, outp, u->us_userlen); 00566 INCPTR(u->us_userlen, outp); 00567 PUTCHAR(u->us_passwdlen, outp); 00568 BCOPY(u->us_passwd, outp, u->us_passwdlen); 00569 00570 pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); 00571 00572 UPAPDEBUG(LOG_INFO, ("pap_sauth: Sent id %d\n", u->us_id)); 00573 00574 TIMEOUT(upap_timeout, u, u->us_timeouttime); 00575 ++u->us_transmits; 00576 u->us_clientstate = UPAPCS_AUTHREQ; 00577 } 00578 00579 00580 /* 00581 * upap_sresp - Send a response (ack or nak). 00582 */ 00583 static void 00584 upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen) 00585 { 00586 u_char *outp; 00587 int outlen; 00588 00589 outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; 00590 outp = outpacket_buf[u->us_unit]; 00591 MAKEHEADER(outp, PPP_PAP); 00592 00593 PUTCHAR(code, outp); 00594 PUTCHAR(id, outp); 00595 PUTSHORT(outlen, outp); 00596 PUTCHAR(msglen, outp); 00597 BCOPY(msg, outp, msglen); 00598 pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); 00599 00600 UPAPDEBUG(LOG_INFO, ("pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate)); 00601 } 00602 00603 #if PPP_ADDITIONAL_CALLBACKS 00604 static char *upap_codenames[] = { 00605 "AuthReq", "AuthAck", "AuthNak" 00606 }; 00607 00608 /* 00609 * upap_printpkt - print the contents of a PAP packet. 00610 */ 00611 static int upap_printpkt( 00612 u_char *p, 00613 int plen, 00614 void (*printer) (void *, char *, ...), 00615 void *arg 00616 ) 00617 { 00618 LWIP_UNUSED_ARG(p); 00619 LWIP_UNUSED_ARG(plen); 00620 LWIP_UNUSED_ARG(printer); 00621 LWIP_UNUSED_ARG(arg); 00622 return 0; 00623 } 00624 #endif /* PPP_ADDITIONAL_CALLBACKS */ 00625 00626 #endif /* PAP_SUPPORT */ 00627 00628 #endif /* PPP_SUPPORT */
Generated on Tue Jul 12 2022 19:20:50 by 1.7.2