--=-P2+ghuDDnqESZ+SiZ5L8 Content-Type: text/plain Content-Transfer-Encoding: 7bit Hi, (cross-post to netfilter-devel & netfilter) As per James' request, I've written a cuseeme nat module for netfilter/iptables. Since I am not a CuSeeMe user (and I don't even have a webcam or any other video conferencing hardware), I'd like some more people to test it out and report their success/failure stories to me privately, before I submit this code for patch-o-matic. Discussions about the code are best kept on netfilter-devel though, etc... At the very least, the module should allow SNAT/MASQUERADE like the 2.2 ip_masq_cuseeme module. DNAT should also work. Some points of attention: - My patch requires the newnat-udp-helper.patch written by Brian J. Murrell that is currently pending for kernel inclusion, as it makes use of "ip_nat_mangle_udp_packet()". I guess that patch won't make it in 2.4.20, so you'll have to get it from CVS (patch-o-matic/pending/newnat-udp-helper.patch) or here: http://cvs.netfilter.org/cgi-bin/cvsweb/netfilter/patch-o-matic/pending/newnat-udp-helper.patch and apply it manually. - You will find that there is only an ip_nat_cuseeme module. Since there are no random ports being opened, but payloads do contain embedded IP addresses, there is only a need for packet payload "mangling" in NAT scenarios. I was quite happy to find out that netfilter/iptables happily supports this! - If you're running into problems, please let me know. I will ask you to recompile with debugging enabled (change the "#if 0" to "#if 1" in ip_nat_cuseeme.c), and capture traffic on both sides of the NAT box, so you know ahead. I am curious if a lot of people will be getting many messages like this in their system logs: expected incoming client 195.162.210.199:7648, but got 0.0.0.0:7648 This shouldn't happen, but from what I get from the specs, it's not always strictly necessary to follow the specs to get a working implementation. Regards, Filip ps. Unless someone else has started working on this, I will probably end up writing a conntrack/nat helper for Microsoft MSN Messenger, now that I still have a Windows partition on a test box, so feel free to volunteer as a tester for that too :-) -----Original Message----- From: James Bryan [mailto:james@penguintowne.com] Sent: Sun 27/10/2002 5:47 To: Sneppe Filip Cc: Subject: RE: Question about Porting the cu-seeme ipchains module to iptables. Filip, I haven't found anyone else yet who's ported the module or is working on it. I'd be delighted if you could port it over to a conntrack/nat module. Regards, James --=-P2+ghuDDnqESZ+SiZ5L8 Content-Disposition: attachment; filename=diff.netfilter.cuseeme.20021104-1 Content-Transfer-Encoding: quoted-printable Content-Type: text/x-patch; name=diff.netfilter.cuseeme.20021104-1; charset=ANSI_X3.4-1968 diff -urN -X dontdiff linux-2.4.20-pre11/Documentation/Configure.help linux= -2.4.20-pre11-msn/Documentation/Configure.help --- linux-2.4.20-pre11/Documentation/Configure.help 2002-10-29 02:18:22.000= 000000 +0100 +++ linux-2.4.20-pre11-msn/Documentation/Configure.help 2002-11-05 01:57:49= .000000000 +0100 @@ -2508,6 +2508,17 @@ If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. If unsure, say `N'. =20 +CuSeeMe protocol support +CONFIG_IP_NF_CUSEEME + The CuSeeMe conferencing protocol is problematic when used in + conjunction with NAT; even though there are no random ports used for + extra connections, the messages contain IP addresses inside them. + This NAT helper mangles the IP address inside packets so both + parties don't get confused. + + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `Y'. + IRC Send/Chat protocol support CONFIG_IP_NF_IRC There is a commonly-used extension to IRC called diff -urN -X dontdiff linux-2.4.20-pre11/include/linux/netfilter_ipv4/ip_co= nntrack_cuseeme.h linux-2.4.20-pre11-msn/include/linux/netfilter_ipv4/ip_co= nntrack_cuseeme.h --- linux-2.4.20-pre11/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h = 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.20-pre11-msn/include/linux/netfilter_ipv4/ip_conntrack_cuseem= e.h 2002-11-04 18:20:59.000000000 +0100 @@ -0,0 +1,68 @@ +#ifndef _IP_CT_CUSEEME +#define _IP_CT_CUSEEME + +#define CUSEEME_PORT 7648 + +/* These structs come from the 2.2 ip_masq_cuseeme code... */ + +/* CuSeeMe data header */ +struct cu_header { + u_int16_t dest_family; + u_int16_t dest_port; + u_int32_t dest_addr; + u_int16_t family; + u_int16_t port; + u_int32_t addr; + u_int32_t seq; + u_int16_t msg; + u_int16_t data_type; + /* possible values: + * 1 small video + * 2 big video + * 3 audio + * 100 acknowledge connectivity when there + * is nothing else to send + * 101 OpenContinue packet + * 104 display a text message and=20 + * disconnect (used by reflector to + * kick clients off) + * 105 display a text message (welcome + * message from reflector) + * 106 exchanged among reflectors for + * reflector interoperation + * 107 carry aux stream data when there is + * no video to piggy-back on + * 108 obsolete (used in Mac alpha version) + * 109 obsolete (used in Mac alpha version) + * 110 used for data rate control + * 111 used for data rate control + * 256 aux data control messages + * 257 aux data packets + * */ + u_int16_t packet_len; +}; + +/* Open Continue Header */ +struct oc_header { + struct cu_header cu_head; + u_int16_t client_count; /* Number of client info structs */ + u_int32_t seq_no; + char user_name[20]; + char stuff[4]; /* Flags, version stuff, etc */ +}; + +/* Client info structures */ +struct client_info { + u_int32_t address; /* Client address */ + u_int32_t stuff[8]; /* Flags, pruning bitfield, packet counts, etc */ +}; + +/* This structure is per expected connection */ +struct ip_ct_cuseeme_expect { +}; + +/* This structure exists only once per master */ +struct ip_ct_cuseeme_master { +}; + +#endif /* _IP_CT_CUSEEME */ diff -urN -X dontdiff linux-2.4.20-pre11/net/ipv4/netfilter/Config.in linux= -2.4.20-pre11-msn/net/ipv4/netfilter/Config.in --- linux-2.4.20-pre11/net/ipv4/netfilter/Config.in 2002-10-29 02:19:16.000= 000000 +0100 +++ linux-2.4.20-pre11-msn/net/ipv4/netfilter/Config.in 2002-10-30 00:52:42= .000000000 +0100 @@ -8,6 +8,7 @@ if [ "$CONFIG_IP_NF_CONNTRACK" !=3D "n" ]; then dep_tristate ' FTP protocol support' CONFIG_IP_NF_FTP $CONFIG_IP_NF_CON= NTRACK dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CON= NTRACK + dep_tristate ' CuSeeMe protocol support' CONFIG_IP_NF_CUSEEME $CONFIG_I= P_NF_CONNTRACK fi =20 if [ "$CONFIG_EXPERIMENTAL" =3D "y" ]; then @@ -67,6 +69,13 @@ define_tristate CONFIG_IP_NF_NAT_IRC $CONFIG_IP_NF_NAT fi fi + if [ "$CONFIG_IP_NF_CUSEEME" =3D "m" ]; then + define_tristate CONFIG_IP_NF_NAT_CUSEEME m + else + if [ "$CONFIG_IP_NF_CUSEEME" =3D "y" ]; then + define_tristate CONFIG_IP_NF_NAT_CUSEEME $CONFIG_IP_NF_NAT + fi + fi # If they want FTP, set to $CONFIG_IP_NF_NAT (m or y),=20 # or $CONFIG_IP_NF_FTP (m or y), whichever is weaker. Argh. if [ "$CONFIG_IP_NF_FTP" =3D "m" ]; then diff -urN -X dontdiff linux-2.4.20-pre11/net/ipv4/netfilter/Makefile linux-= 2.4.20-pre11-msn/net/ipv4/netfilter/Makefile --- linux-2.4.20-pre11/net/ipv4/netfilter/Makefile 2002-10-29 02:19:16.0000= 00000 +0100 +++ linux-2.4.20-pre11-msn/net/ipv4/netfilter/Makefile 2002-11-02 21:01:26.= 000000000 +0100 @@ -45,6 +53,7 @@ # NAT helpers=20 obj-$(CONFIG_IP_NF_NAT_FTP) +=3D ip_nat_ftp.o obj-$(CONFIG_IP_NF_NAT_IRC) +=3D ip_nat_irc.o +obj-$(CONFIG_IP_NF_NAT_CUSEEME) +=3D ip_nat_cuseeme.o =20 # generic IP tables=20 obj-$(CONFIG_IP_NF_IPTABLES) +=3D ip_tables.o diff -urN -X dontdiff linux-2.4.20-pre11/net/ipv4/netfilter/ip_nat_cuseeme.= c linux-2.4.20-pre11-msn/net/ipv4/netfilter/ip_nat_cuseeme.c --- linux-2.4.20-pre11/net/ipv4/netfilter/ip_nat_cuseeme.c 1970-01-01 01:00= :00.000000000 +0100 +++ linux-2.4.20-pre11-msn/net/ipv4/netfilter/ip_nat_cuseeme.c 2002-11-05 0= 1:45:42.000000000 +0100 @@ -0,0 +1,285 @@ +/* CuSeeMe extension for UDP NAT alteration. + * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be> + * based on ip_masq_cuseeme.c in 2.2 kernels + * + * ip_nat_cuseeme.c v0.0.4 2002-11-04 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * insmod ip_nat_cuseeme.o ports=3Dport1,port2,...port<MAX_PORTS> + * + * Please give the ports of the CuSeeMe traffic you want to track. + * If you don't specify ports, the default will be UDP port 7648. + * + * CuSeeMe Protocol Documentation: + * http://cu-seeme.net/squeek/tech/contents.html + */ + +#include <linux/module.h> +#include <linux/netfilter_ipv4.h> +#include <linux/ip.h> +#include <linux/udp.h> + +#include <linux/netfilter.h> +#include <linux/netfilter_ipv4/ip_tables.h> +#include <linux/netfilter_ipv4/ip_conntrack_helper.h> +#include <linux/netfilter_ipv4/ip_conntrack_cuseeme.h> +#include <linux/netfilter_ipv4/ip_nat_helper.h> +#include <linux/netfilter_ipv4/ip_nat_rule.h> + +MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>"); +MODULE_DESCRIPTION("Netfilter NAT helper for CuSeeMe"); +MODULE_LICENSE("GPL"); + +#define MAX_PORTS 8 + +static int ports[MAX_PORTS]; +static int ports_c =3D 0; +#ifdef MODULE_PARM +MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of CuSeeMe hosts"); +#endif + +#if 0=20 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* process packet from client->reflector, possibly manipulate client IP in= payload */ +void cuseeme_mangle_outgoing(struct ip_conntrack *ct, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb, + char *data, + unsigned int datalen) +{ + char new_port_ip[6]; + struct cu_header *cu_head=3D(struct cu_header *)data; +=09 + DEBUGP("ip_nat_cuseeme: outgoing packet, ID %u, dest_family %u\n",=20 + ntohs(cu_head->data_type), ntohs(cu_head->dest_family)); + =09 + /* At least check that the data at offset 10 is the client's port and IP = address */ + if ((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip =3D=3D cu_head->addr)= && + (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port =3D=3D cu_hea= d->port)) { + DEBUGP("ip_nat_cuseeme: rewrite outgoing client %u.%u.%u.%u:%u->%u.%u.%u= .%u:%u at offset 10\n",=20 + NIPQUAD(cu_head->addr), + ntohs(cu_head->port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port)); + *((u_int16_t *)new_port_ip) =3D ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst= .u.udp.port; + *((u_int32_t *)(new_port_ip+2)) =3D ct->tuplehash[IP_CT_DIR_REPLY].tuple= .dst.ip; + /* at offset 10, replace 6 bytes containing port + IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + 10, 6, (char *)(new_port_ip), 6); + } else=20 + if (net_ratelimit()) + printk("ip_nat_cuseeme: expected outgoing client %u.%u.%u.%u:%u, but go= t %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port), + NIPQUAD(cu_head->addr), + ntohs(cu_head->port)); +} + +/* process packet from reflector->client, possibly manipulate client IP & = reflector IP in payload */ +void cuseeme_mangle_incoming(struct ip_conntrack *ct, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb, + char *data, + unsigned int datalen) +{ + char new_port_ip[6]; + struct cu_header *cu_head =3D (struct cu_header *)data; + struct oc_header *oc_head =3D (struct oc_header *)data;=20 + struct client_info *ci;=20 + unsigned int client_info_size =3D sizeof(struct oc_header); + int i; +=09 + DEBUGP("ip_nat_cuseeme: incoming packet, ID %u, dest_family %u\n",=20 + ntohs(cu_head->data_type), ntohs(cu_head->dest_family)); + =09 + /* Check if we're really dealing with the client's port + IP address befo= re rewriting */ + if((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip =3D=3D cu_head->dest_addr= ) && + (ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port =3D=3D cu_head->d= est_port)) { + DEBUGP("ip_nat_cuseeme: rewrite incoming client %u.%u.%u.%u:%u->%u.%u.%u= .%u:%u at offset 2\n", + NIPQUAD(cu_head->dest_addr), + ntohs(cu_head->dest_port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port)); + *((u_int16_t *)new_port_ip) =3D ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.= src.u.udp.port; + *((u_int32_t *)(new_port_ip+2)) =3D ct->tuplehash[IP_CT_DIR_ORIGINAL].tu= ple.src.ip; + /* at offset 2, replace 6 bytes containing port + IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + 2, 6, (char *)(new_port_ip), 6); + } else=20 + if (net_ratelimit()) + printk("ip_nat_cuseeme: expected incoming client %u.%u.%u.%u:%u, but go= t %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port), + NIPQUAD(cu_head->dest_addr), + ntohs(cu_head->dest_port)); +=09 + /* Check if we're really dealing with the server's port + IP address befo= re rewriting.=20 + In some cases, the IP address =3D=3D 0.0.0.0 so we don't rewrite anyth= ing */ + if((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip =3D=3D cu_head->addr) && + (ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port =3D=3D cu_head->p= ort)) { + DEBUGP("in_nat_cuseeme: rewrite incoming server %u.%u.%u.%u:%u->%u.%u.%u= .%u:%u at offset 10\n", + NIPQUAD(cu_head->addr), + ntohs(cu_head->port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip), + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port)); + *((u_int16_t *)new_port_ip) =3D ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.= dst.u.udp.port; + *((u_int32_t *)(new_port_ip+2)) =3D ct->tuplehash[IP_CT_DIR_ORIGINAL].tu= ple.dst.ip; + /* at offset 10, replace 6 bytes containing port + IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + 10, 6, (char *)(new_port_ip), 6); + } else=20 + /* Sometimes we find 0.0.0.0, sometimes an IP address - the docs say thi= s field + is not that important so we're not logging this unless we're debuggin= g */ + DEBUGP("ip_nat_cuseeme: no biggie, expected incoming server %u.%u.%u.%u:= %u, but got %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port), + NIPQUAD(cu_head->addr), + ntohs(cu_head->port)); +=09 + /* Spin through client_info structs until we find our own */ + if((ntohs(cu_head->data_type) =3D=3D 101) && (datalen >=3D sizeof(struct = oc_header))) { +/* oc_head =3D (struct oc_header *) &udph[1]; */ +/* client_info_size =3D sizeof(struct oc_header); */ + DEBUGP("ip_nat_cuseeme: looping through %u client_info structs\n", oc_he= ad->client_count); + for(i=3D0;=20 + (i<oc_head->client_count) &&=20 + (sizeof(struct client_info)+client_info_size<=3Ddatalen);=20 + ++i, client_info_size+=3Dsizeof(struct client_info)) { + ci=3D(struct client_info *)(cu_head+client_info_size); + DEBUGP("ip_nat_cuseeme: here's a possible address: %u.%u.%u.%u\n", NIPQ= UAD(ci->address)); + if(ci->address =3D=3D ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip) { + /* mangle this IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + (unsigned int)((void *)&ci->address - (void *= )cu_head), 4,=20 + (char *)(&ct->tuplehash[IP_CT_DIR_ORIGINAL].t= uple.src.ip), 4); + break; + } + } + } +} + +static unsigned int=20 +cuseeme_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + struct iphdr *iph =3D (*pskb)->nh.iph; + struct udphdr *udph =3D (void *)iph + iph->ihl * 4; + int dir =3D CTINFO2DIR(ctinfo); + unsigned int datalen =3D (*pskb)->len - iph->ihl * 4 - sizeof(struct udph= dr); + char *data =3D (char *) &udph[1]; +=09 + DEBUGP("ip_nat_cuseeme: cuseeme_nat_help, direction: %s hook: %s\n", + dir =3D=3D IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum =3D=3D NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum =3D=3D NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum =3D=3D NF_IP_LOCAL_OUT ? "OUTPUT" : "???" + ); +/* DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); */ +/* DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); */ +=09 + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + if (!((hooknum =3D=3D NF_IP_POST_ROUTING && dir =3D=3D IP_CT_DIR_ORIGINAL= ) + || (hooknum =3D=3D NF_IP_PRE_ROUTING && dir =3D=3D IP_CT_DIR_REPLY)))= { + DEBUGP("ip_nat_cuseeme: not touching dir %s at hook %s\n", + dir =3D=3D IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum =3D=3D NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum =3D=3D NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum =3D=3D NF_IP_LOCAL_OUT ? "OUTPUT" : "????"); + return NF_ACCEPT; + } +=09 + if(datalen < sizeof(struct cu_header)) { + /* packet too small */ + if (net_ratelimit()) + printk("ip_nat_cuseeme: payload too small (%u, should be >=3D %u)\n",=20 + datalen, sizeof(struct cu_header)); + return NF_ACCEPT; + } + + + /* In the debugging output, "outgoing" is from client to server, and + "incoming" is from server to client */ + if(HOOK2MANIP(hooknum) =3D=3D IP_NAT_MANIP_SRC)=20 + cuseeme_mangle_outgoing(ct, info, ctinfo, pskb, data, datalen); + else=20 + cuseeme_mangle_incoming(ct, info, ctinfo, pskb, data, datalen); + + return NF_ACCEPT; +} + +static struct ip_nat_helper cuseeme[MAX_PORTS]; +static char cuseeme_names[MAX_PORTS][14]; /* cuseeme-65535 */ + +static void fini(void) +{ + int i; +=09 + for (i =3D 0 ; i < ports_c; i++) { + DEBUGP("ip_nat_cuseeme: unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&cuseeme[i]); + } +} + +static int __init init(void) +{ + int i, ret =3D 0; + char *tmpname; + + if (!ports[0]) + ports[0] =3D CUSEEME_PORT; + =09 + for (i =3D 0 ; (i < MAX_PORTS) && ports[i] ; i++) { + memset(&cuseeme[i], 0, sizeof(struct ip_nat_helper)); + + cuseeme[i].tuple.dst.protonum =3D IPPROTO_UDP; + cuseeme[i].tuple.dst.u.udp.port =3D htons(ports[i]); + cuseeme[i].mask.dst.protonum =3D 0xFFFF; + cuseeme[i].mask.dst.u.udp.port =3D 0xFFFF; + cuseeme[i].help =3D cuseeme_nat_help; + cuseeme[i].flags =3D IP_NAT_HELPER_F_STANDALONE +=20 + IP_NAT_HELPER_F_ALWAYS; /* dunno if IP_NAT_HELPER_F_A= LWAYS + is stricly needed... */ + cuseeme[i].me =3D THIS_MODULE; + cuseeme[i].expect =3D NULL; /* cuseeme_nat_expected; */ + =09 + tmpname =3D &cuseeme_names[i][0]; + if (ports[i] =3D=3D CUSEEME_PORT) + sprintf(tmpname, "cuseeme"); + else + sprintf(tmpname, "cuseeme-%d", i); + cuseeme[i].name =3D tmpname; + =09 + DEBUGP("ip_nat_cuseeme: registering helper for port %d: name %s\n", + ports[i], cuseeme[i].name); + ret =3D ip_nat_helper_register(&cuseeme[i]); + =09 + if (ret) { + printk("ip_nat_cuseeme: unable to register helper for port %d\n", + ports[i]); + fini(); + return ret; + } + ports_c++; + } + return ret; +} +=09 +module_init(init); +module_exit(fini); --=-P2+ghuDDnqESZ+SiZ5L8--