#pragma module REDIR "REDIR-1-B" #define __MODULE__ "REDIR" /* **++ ** FACILITY: HTTP Redirector ** ** ** MODULE DESCRIPTION: ** ** This is a main module of the REDIR facility. ** ** AUTHORS: ** ** Ruslan R. Laishev ** ** CREATION DATE: 12-OCT-2009 ** ** DESIGN ISSUES: ** ** ** ** MODIFICATION HISTORY: ** ** 1-DEC-2009 RRL Fixed bug with skipping ARP-multicast by adding ** NMA$C_PCLI_MLT = NMA$C_STATE_ON -- Accept all multicast addresses. ** ** {@tbs@}... **-- */ /* ** ** OpenVMS Includes ** */ #define __NEW_STARLET 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ** TCP/IP protocol basic definitions */ #include "ipdef.h" #include "rdrdef.h" #include "redir_msg.h" #include "macros.h" /* ** Network Device name and Hawrdare Address (MAC address) */ struct dsc$descriptor_d nicdsc; ETH_MAC hwa, broad = {.eth$b_mac = {0xff,0xff,0xff,0xff,0xff,0xff}}; IP4_ADDR homeip,gateway; int ipchan,arpchan; /* ** CLI stuff */ $DESCRIPTOR(p1_dsc, "P1"); $DESCRIPTOR(p2_dsc, "P2"); $DESCRIPTOR(p3_dsc, "P3"); $DESCRIPTOR(p4_dsc, "P4"); $DESCRIPTOR(debug_dsc, "DEBUG"); extern void *REDIR_CLD; int debug_flag = 0; unsigned char buffer[8192], url[255]; #pragma nomember_alignment struct p_listd { int len; void * ptr; }; void mac2str ( ETH_MAC *mac, void *buf ) { sprintf(buf,"%02X-%02X-%02X-%02X-%02X-%02X",mac->eth$b_mac[0],mac->eth$b_mac[1],mac->eth$b_mac[2], mac->eth$b_mac[3],mac->eth$b_mac[4],mac->eth$b_mac[5]); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Put formated message to standard output device (SYS$OUTPUT). ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** msgid: VMS condition code ** variable agriments list ** ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ int _log ( int msgid, ... ) { long status,retvalue = msgid; va_list ap; char buf[4096] = {"!%D "},outadr[4],msg_buf[4096]; struct dsc$descriptor opr_dsc,buf_dsc,fao_dsc; int argc,argl[32],idx,flag=15,lvl; /* ** Get a message text with given msgid */ INIT_SDESC(fao_dsc,sizeof(buf)-4,&buf[4]); if ( !(1 & (status = sys$getmsg (msgid,&fao_dsc.dsc$w_length,&fao_dsc,flag,&outadr))) ) lib$signal(status); /* ** Reorganize parameters list for $FAOL */ va_start(ap,msgid); argl[0] = 0; for (idx = 1,va_count(argc);idx < argc;idx++) argl[idx] = va_arg(ap,unsigned); va_end((void *) msgid); /* ** Format a message, put it to SYS$OUTPUT */ fao_dsc.dsc$a_pointer -=4; fao_dsc.dsc$w_length +=4; INIT_SDESC(buf_dsc, sizeof(msg_buf),msg_buf); if ( !(1 & (status = sys$faol(&fao_dsc,&buf_dsc.dsc$w_length,&buf_dsc,argl))) ) lib$signal(status); lib$put_output(&buf_dsc); return retvalue; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Processing command line parameters. ** ** FORMAL PARAMETERS: ** ** None. ** ** RETURN VALUE: ** ** VMS Condition code ** ** **-- */ int redir (void) { int status; char buf[32]; struct dsc$descriptor junk; /* ** /DEBUG - ? */ debug_flag = CLI$_PRESENT == cli$present (&debug_dsc); /* ** P1/NICDEV - Network device name */ INIT_DDESC(nicdsc); if ( !(1 & (status = cli$get_value(&p1_dsc,&nicdsc,&nicdsc.dsc$w_length))) ) return lib$signal(status); /* ** P2/Home IP address */ INIT_SDESC(junk,sizeof(buf),buf); if ( !(1 & (status = cli$get_value(&p2_dsc,&junk,&junk.dsc$w_length))) ) return lib$signal(status); buf[junk.dsc$w_length] = '\0'; inet_aton(buf,&homeip); /* ** P3/Default Gateway IP address */ INIT_SDESC(junk,sizeof(buf),buf); if ( !(1 & (status = cli$get_value(&p3_dsc,&junk,&junk.dsc$w_length))) ) return lib$signal(status); buf[junk.dsc$w_length] = '\0'; inet_aton(buf,&gateway); /* ** URL to redirect */ INIT_SDESC(junk,sizeof(url),url); if ( !(1 & (status = cli$get_value(&p4_dsc,&junk,&junk.dsc$w_length))) ) return lib$signal(status); if ( url[0] == '"' ) memmove(&url,&url[1], junk.dsc$w_length -= 2); url[junk.dsc$w_length] = '\0'; return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Perfroms ARP Gratuitous - ARP Announcing ** ** FORMAL PARAMETERS: ** ** None. ** ** RETURN VALUE: ** ** VMS Condition code ** ** **-- */ void arp_proc (void) { int status; IOSB iosb; struct {short pcli_fmt; int fmt; short pcli_tpy; int pty; short pcli_pad; int pad; short pcli_mlt; int mlt; } arp_p = { NMA$C_PCLI_FMT, NMA$C_LINFM_ETH, /* Ethernet II frame format */ NMA$C_PCLI_PTY,0x0608, /* Protocol Type is ARP */ NMA$C_PCLI_PAD,NMA$C_STATE_OFF, /* Don't prefix by length data block */ NMA$C_PCLI_MLT,NMA$C_STATE_ON }; struct p_listd p_dsc = {sizeof(arp_p),&arp_p}; ARP_PDU arp,*parp = (ARP_PDU *) &buffer; NMA_ATTR *attr = &buffer; HDR_ETH2 ehdr; char tha[32],sha[32],tpa[32],spa[32]; /* ** Start a channel to the NIC */ if ( !(1 & (status = sys$assign(&nicdsc,&arpchan,0,0))) ) lib$signal(status); if ( !(1 & (status = sys$qiow(EFN$C_ENF,arpchan,IO$_SETMODE|IO$M_CTRL|IO$M_STARTUP, &iosb,0,0,0,&p_dsc,0,0,0,0))) || !(1 & iosb.iosb$w_status) ) lib$signal((1 & status)?iosb.iosb$w_status:status); /* * Issue the SENSEMODE QIO to get our physical address for the ARP messages. */ p_dsc.len = sizeof(buffer); p_dsc.ptr = &buffer; if ( !(1 & (status = sys$qiow(EFN$C_ENF,arpchan,IO$_SENSEMODE|IO$M_CTRL, &iosb,0,0,0,&p_dsc,0,0,0,0))) || !(1 & iosb.iosb$w_status) ) lib$signal((1 & status)?iosb.iosb$w_status:status); /* ** Run over returned attributes list to find the NMA$C_PCLI_PHA */ for(int i = 0; i < iosb.iosb$w_bcnt; ) { DEBUG("offset = %u,attr->nma$v_pid = %u\n",i,attr->nma$v_pid); if ( attr->nma$v_pid == NMA$C_PCLI_PHA ) { memcpy(&hwa,attr->nma$b_prm,sizeof(ETH_MAC)); break; } else { if ( attr->nma$v_str ) { DEBUG("attr->nma$b_prm[0:%u] = '%.*s'\n", attr->nma$w_bcnt,attr->nma$w_bcnt,attr->nma$b_prm); i += (NMA$K_STRSZ + attr->nma$w_bcnt); } else { DEBUG("attr->nma$l_prm = %u/%x\n", attr->nma$l_prm,attr->nma$l_prm); i += NMA$K_LWSZ; } attr = (NMA_ATTR *) &buffer[i]; } } /* ** Build ARP packet and send it to default gateway/router */ arp.arp$w_hrd = htons(1); arp.arp$w_proto = htons(PTY$K_IP); arp.arp$b_hln = sizeof(ETH_MAC); arp.arp$b_pln = sizeof(IP4_ADDR); arp.arp$w_op = htons(ARP$K_REQUEST); memcpy(&arp.arp$r_tha,&broad,sizeof(ETH_MAC)); memcpy(&arp.arp$r_tpa,&gateway,sizeof(IP4_ADDR)); memcpy(&arp.arp$r_sha,&hwa,sizeof(ETH_MAC)); memcpy(&arp.arp$r_spa,&homeip,sizeof(IP4_ADDR)); if ( !(1 & (status = sys$qiow(EFN$C_ENF,arpchan,IO$_WRITEVBLK,&iosb,0,0,&arp, sizeof(ARP_PDU),0,0,&broad,0))) || !(1 & iosb.iosb$w_status) ) lib$signal((1 & status)?iosb.iosb$w_status:status); /* ** Start listening for ARP request for the our Home IP, ** answering with our MAC Address */ while (1) { if ( !(1 & (status = sys$qiow(EFN$C_ENF,arpchan,IO$_READVBLK,&iosb,0,0,&buffer, sizeof(buffer),0,0,&ehdr,0))) || !(1 & iosb.iosb$w_status) ) lib$signal((1 & status)?iosb.iosb$w_status:status); mac2str(&parp->arp$r_tha,tha); mac2str(&parp->arp$r_sha,sha); inet_ntop (AF_INET,&parp->arp$r_tpa,tpa,sizeof(tpa)); inet_ntop (AF_INET,&parp->arp$r_spa,spa,sizeof(spa)); _log(REDIR_RARPPKT,ntohs(parp->arp$w_op),sha,spa,tha,tpa); if ( parp->arp$w_op != htons(ARP$K_REQUEST) ) continue; if ( memcmp(&parp->arp$r_tpa,&homeip,sizeof(IP4_ADDR)) ) continue; /* ** Build ARP Reply packet */ arp.arp$w_op = htons(ARP$K_REPLY); memcpy(&arp.arp$r_tha,&parp->arp$r_sha,sizeof(ETH_MAC)); memcpy(&arp.arp$r_tpa,&parp->arp$r_spa,sizeof(IP4_ADDR)); if ( !(1 & (status = sys$qiow(EFN$C_ENF,arpchan,IO$_WRITEVBLK,&iosb,0,0,&arp, sizeof(ARP_PDU),0,0,&arp.arp$r_tha,0))) || !(1 & iosb.iosb$w_status) ) lib$signal((1 & status)?iosb.iosb$w_status:status); mac2str(&arp.arp$r_tha,tha); mac2str(&arp.arp$r_sha,sha); inet_ntop (AF_INET,&arp.arp$r_tpa,tpa,sizeof(tpa)); inet_ntop (AF_INET,&arp.arp$r_spa,spa,sizeof(spa)); _log(REDIR_SARPPKT,ntohs(arp.arp$w_op),sha,spa,tha,tpa); } return SS$_NORMAL; } unsigned short ipchksum( IP_DGRAM *ipdg, unsigned short len ) { unsigned short *sdata = (unsigned short *) ipdg; unsigned acc = 0; ipdg->ip$w_csum = 0; for (; len > 1; len -= 2) acc += *sdata++; /* ** Add up any odd byte */ if ( len ) acc += (unsigned short)(*(unsigned char *)sdata); acc = (acc >> 16) + (acc & 0xFFFF); acc += (acc >> 16); return ~acc; } unsigned short tcpchksum( IP_DGRAM * ipdg, TCP_SEG * seg, unsigned short len ) { unsigned short *sdata = (unsigned short *) seg; int acc = 0; seg->tcp$w_csum = 0; /* ** Compute TCP pseudo header check sum */ acc += (unsigned short) htons(ipdg->ip$b_proto); acc += (unsigned short) htons(len); sdata = (unsigned short *) &ipdg->ip$r_src; acc += *sdata++; acc += *sdata++; sdata = (unsigned short *) &ipdg->ip$r_dst; acc += *sdata++; acc += *sdata++; for ( sdata = (unsigned short *) seg; len > 1; len -= 2) acc += *sdata++; /* ** add up any odd byte */ if ( len ) acc += (unsigned short)(*(unsigned char *)sdata); /* ** DISK$SYSMAN:[V82.LAN.LIS]LANACP_DLL.LIS, in_chksum() */ acc = (acc >> 16) + (acc & 0xFFFF); acc += (acc >> 16); return ~acc; } void main (void) { unsigned status = 0,seq = 0; IOSB iosb; HDR_ETH2 ehdr,outehdr; IP_DGRAM * ipdg = (IP_DGRAM *) &buffer; TCP_SEG * seg = &ipdg->ip$r_seg; unsigned char hthead [] ={"HTTP/1.1 307 Temporary redirect\r\n"\ "Location: %s\r\n"\ "Content-Type: text/html\r\n"\ "Content-Length: %u\r\n\r\n%s"}, htbody [] ={"OpenVMS forever !!! StarLet Team"\ ""}; struct {short pcli_fmt; int fmt; short pcli_tpy; int pty; short pcli_pad; int pad;} ip_p = { NMA$C_PCLI_FMT, NMA$C_LINFM_ETH, /* Ethernet II frame format */ NMA$C_PCLI_PTY,0x0008, /* Protocol Type is IP */ NMA$C_PCLI_PAD,NMA$C_STATE_OFF /* Don't prefix by length data block */ }; struct p_listd p_dsc = {sizeof(ip_p),&ip_p}; char buf[255] = {"REDIR "}; struct dsc$descriptor cmd_dsc; pthread_t tid; pthread_attr_t tattr; /* ** Initalize attributes for threads */ status = pthread_attr_init(&tattr); status = pthread_attr_setdetachstate(&tattr,PTHREAD_CREATE_JOINABLE); status = pthread_attr_setstacksize(&tattr,128000); /* ** Process command line arguments */ INIT_SDESC(cmd_dsc,sizeof(buf)-6,buf+6); if ( !(1 & (status = lib$get_foreign(&cmd_dsc,0,&cmd_dsc.dsc$w_length,&status))) ) return status; cmd_dsc.dsc$w_length += 6; cmd_dsc.dsc$a_pointer -= 6; if ( CLI$_NORMAL == (status = cli$dcl_parse (&cmd_dsc,&REDIR_CLD,0,0,0)) ) status = cli$dispatch(); if ( !(1 & status) ) sys$exit(status); _log(REDIR_HOMEIP,homeip.ip4$b_addr[0],homeip.ip4$b_addr[1], homeip.ip4$b_addr[2],homeip.ip4$b_addr[3]); _log(REDIR_URL,url); // arp_proc(); if ( status = pthread_create(&tid,&tattr,(void *)arp_proc,NULL) ) perror("thread"); sleep(1); _log(REDIR_NICDEV,&nicdsc,hwa.eth$b_mac[0],hwa.eth$b_mac[1],hwa.eth$b_mac[2], hwa.eth$b_mac[3],hwa.eth$b_mac[4],hwa.eth$b_mac[5]); /* ** Start a channel to the NIC */ if ( !(1 & (status = sys$assign(&nicdsc,&ipchan,0,0))) ) lib$signal(status); if ( !(1 & (status = sys$qiow(EFN$C_ENF,ipchan,IO$_SETMODE|IO$M_CTRL|IO$M_STARTUP, &iosb,0,0,0,&p_dsc,0,0,0,0))) || !(1 & iosb.iosb$w_status) ) lib$signal((1 & status)?iosb.iosb$w_status:status); /* ** */ for(int i = 0;; i++) { if ( !(1 & (status = sys$qiow(EFN$C_ENF,ipchan,IO$_READVBLK,&iosb,0,0,&buffer, sizeof(buffer),0,0,&ehdr,0))) || !(1 & iosb.iosb$w_status) ) lib$signal((1 & status)?iosb.iosb$w_status:status); { char src[16],dst[16]; inet_ntop (AF_INET,&ipdg->ip$r_dst,dst,sizeof(dst)); inet_ntop (AF_INET,&ipdg->ip$r_src,src,sizeof(src)); _log(REDIR_RCVD,src,ntohs(seg->tcp$w_spn), dst,ntohs(seg->tcp$w_dpn), iosb.iosb$w_bcnt, ntohl(seg->tcp$l_seq), ntohl(seg->tcp$l_ack)); } /* ** Got SYN... */ status = ipdg->ip$r_src.ip4$l_addr; ipdg->ip$r_src.ip4$l_addr = ipdg->ip$r_dst.ip4$l_addr; ipdg->ip$r_dst.ip4$l_addr = status; status = seg->tcp$w_spn; seg->tcp$w_spn = seg->tcp$w_dpn; seg->tcp$w_dpn = status; seg->tcp$v_hlen = TCP$K_SZ/4; status = seg->tcp$l_ack; seg->tcp$l_ack = htonl(1 + ntohl(seg->tcp$l_seq)); seg->tcp$l_seq = status; if ( seg->tcp$v_syn ) { seg->tcp$b_ctl |= (TCP$M_ACK | TCP$M_PSH); status = sprintf(&seg->tcp$b_data,&hthead,url,sizeof(htbody)-1,htbody); ipdg->ip$w_len = htons(TCP$K_SZ + IP$K_SZ + status); seg->tcp$l_seq = 0; seq = status; } else { seg->tcp$b_ctl = (TCP$M_RST | TCP$M_FIN | TCP$M_ACK); ipdg->ip$w_len = htons(TCP$K_SZ + IP$K_SZ); // seg->tcp$l_seq = htonl(seq); } /* ** Calculate check sums and send */ ipdg->ip$w_csum = ipchksum(ipdg,ipdg->ip$v_hlen*4); seg->tcp$w_csum = tcpchksum(ipdg,seg,ntohs(ipdg->ip$w_len) - ipdg->ip$v_hlen*4); if ( !(1 & (status = sys$qiow(EFN$C_ENF,ipchan,IO$_WRITEVBLK,&iosb,0,0,ipdg, ntohs(ipdg->ip$w_len),0,0,&ehdr.eth2$r_sa,0))) || !(1 & iosb.iosb$w_status) ) lib$signal((1 & status)?iosb.iosb$w_status:status); { char src[16],dst[16]; inet_ntop (AF_INET,&ipdg->ip$r_dst,dst,sizeof(dst)); inet_ntop (AF_INET,&ipdg->ip$r_src,src,sizeof(src)); _log(REDIR_SENT,src,ntohs(seg->tcp$w_spn), dst,ntohs(seg->tcp$w_dpn), iosb.iosb$w_bcnt, ntohl(seg->tcp$l_seq), ntohl(seg->tcp$l_ack)); } } }