www.pudn.com > PIPMasq.zip > dumpPacket.cpp
/////////////////// DumpPacket() /////////////////////////////// #include "stdafx.h" #include#define SNAP_PKT_TYPE 0xaaaa #define ARP_PKT_TYPE 0x0806 #define IP_PKT_TYPE 0x0800 #define UDP_PKT_TYPE 17 #define TCP_PKT_TYPE 6 #define ICMP_PKT_TYPE 1 char szProtocolName[80]; char buffer[256]; void OutPut(char *ptr) { printf("%s",ptr); } int EtherHeader(char far* lpFrameBuffer,int Pointer,int FrameLength,FILE *OutFp,int *EtherType) { int Etype; OutPut("\n#=== ETHERNET HEADER"); sprintf(buffer,"\nETH: DST ADDR = [%02x:%02x:%02x:%02x:%02x:%02x]", (unsigned int) lpFrameBuffer[Pointer] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+1] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+2] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+3] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+4] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+5] & 0x00ff); OutPut(buffer); sprintf(buffer,"\nETH: SRC ADDR = [%02x:%02x:%02x:%02x:%02x:%02x]", (unsigned int) lpFrameBuffer[Pointer+6] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+7] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+8] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+9] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+10] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+11] & 0x00ff); OutPut(buffer); Etype = (unsigned int) lpFrameBuffer[Pointer+12] & 0x00ff; Etype<<=8; Etype|=((unsigned int) lpFrameBuffer[Pointer+13] & 0x00ff); if( Etype < 1514 ) { sprintf(buffer,"\nETH: Length = [%04d]",Etype); OutPut(buffer); Etype = SNAP_PKT_TYPE; } else { sprintf(buffer,"\nETH: TYPE = [%02x%02x]", (unsigned int) lpFrameBuffer[Pointer+12] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+13] & 0x00ff ); OutPut(buffer); } *EtherType = Etype; return(Pointer +14 ) ; } int SNAPHeader(char far *lpFrameBuffer,int Pointer,int FrameLength,FILE *OutFp,int *EtherType) { unsigned int DSap = (unsigned int)lpFrameBuffer[Pointer] & 0x00ff; unsigned int SSap = (unsigned int)lpFrameBuffer[Pointer+1] & 0x00ff; unsigned int Ctrl = (unsigned int)lpFrameBuffer[Pointer+2] & 0x00ff; unsigned int Etype =(unsigned int)lpFrameBuffer[Pointer+6] * 0x100 + (unsigned int)lpFrameBuffer[Pointer+7] & 0x00ff; if( DSap != 0xaa || SSap != 0xaa || Ctrl != 3 ) { OutPut("\n#=== IEEE802.2 HEADER"); sprintf(buffer,"\nLLC: DSAP = [0x%04x]",DSap); OutPut(buffer); sprintf(buffer,"\nLLC: SSAP = [0x%04x]",SSap); OutPut(buffer); sprintf(buffer,"\nLLC: Control = %d",Ctrl); OutPut(buffer); *EtherType = Ctrl; sprintf(szProtocolName,"IEEE802.2"); return(Pointer+3); } OutPut("\n#=== SNAP HEADER"); sprintf(buffer,"\nLLC: DSAP = [0x%04x]",DSap); OutPut(buffer); sprintf(buffer,"\nLLC: SSAP = [0x%04x]",SSap); OutPut(buffer); sprintf(buffer,"\nLLC: Control = UI"); OutPut(buffer); sprintf(buffer,"\nLLC: Porotocl ID =[%02x%02x%2x] ", (unsigned int) lpFrameBuffer[Pointer+3] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+4] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+5] & 0x00ff ); sprintf(buffer,"\nLLC: Frame Type = [%04x]",Etype); OutPut(buffer); *EtherType = Etype; sprintf(szProtocolName,"Other"); return(Pointer+6); } int ARPHeader(char far *lpFrameBuffer,int Pointer,int FrameLength,FILE *OutFp) { unsigned int HAddrType = (unsigned int)lpFrameBuffer[Pointer+1] + (unsigned int)lpFrameBuffer[Pointer]*0x100; unsigned int PAddrType = (unsigned int)lpFrameBuffer[Pointer+3] + (unsigned int)lpFrameBuffer[Pointer+2]*0x100; unsigned int HAddrLen =( unsigned int)lpFrameBuffer[Pointer+4]; unsigned int PAddrLen =( unsigned int)lpFrameBuffer[Pointer+5]; unsigned int OpCode = (unsigned int)lpFrameBuffer[Pointer+7] + (unsigned int)lpFrameBuffer[Pointer+6]*0x100; OutPut("\n#=== ARP HEADER"); sprintf(buffer,"\nARP: Hardware Addr Type = [0x%04x]",HAddrType); OutPut(buffer); sprintf(buffer,"\nARP: Protocol Addr Type = [0x%04x]",PAddrType); OutPut(buffer); sprintf(buffer,"\nARP: Hardware Addr Len = [%2d]",HAddrLen); OutPut(buffer); sprintf(buffer,"\nARP: Protocol Addr Len = [%2d]",PAddrLen); OutPut(buffer); sprintf(buffer,"\nARP: Operation Code = [%s]", OpCode == 1 ? "REQUEST" : OpCode == 2 ? "REPRY" :"UNKWON"); OutPut(buffer); sprintf(buffer,"\nARP: SRC MAC ADDR = [%02x:%02x:%02x:%02x:%02x:%02x]", (unsigned int) lpFrameBuffer[Pointer+8] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+9] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+10] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+11] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+12] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+13] & 0x00ff); OutPut(buffer); sprintf(buffer,"\nARP: SRC IP ADDR = [%03u.%03u.%03u.%03u]", lpFrameBuffer[Pointer+14] & 0x00ff, lpFrameBuffer[Pointer+15] & 0x00ff, lpFrameBuffer[Pointer+16] & 0x00ff, lpFrameBuffer[Pointer+17] & 0x00ff); OutPut(buffer); sprintf(buffer,"\nARP: TAGT MAC ADDR = [%02x:%02x:%02x:%02x:%02x:%02x]", (unsigned int) lpFrameBuffer[Pointer+18] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+19] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+20] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+21] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+22] & 0x00ff, (unsigned int) lpFrameBuffer[Pointer+23] & 0x00ff); OutPut(buffer); sprintf(buffer,"\nARP: TAGT IP ADDR = [%03u.%03u.%03u.%03u]", lpFrameBuffer[Pointer+24] & 0x00ff, lpFrameBuffer[Pointer+25] & 0x00ff, lpFrameBuffer[Pointer+26] & 0x00ff, lpFrameBuffer[Pointer+27] & 0x00ff); OutPut(buffer); return(Pointer+28); } int IPHeader(char far *lpFrameBuffer,int Pointer,int FrameLength,FILE *OutFp,int *ProtType) { int HeaderLength = ((int ) lpFrameBuffer[Pointer] & 0x0f ) * 4; int Version = ((int) lpFrameBuffer[Pointer] >> 4 ) & 0x000f; int Service = (int) lpFrameBuffer[Pointer+1] & 0x00ff; int TotalLength = ((int) lpFrameBuffer[Pointer+2]* 0x100) + lpFrameBuffer[Pointer+3]; int Identification = ((int) lpFrameBuffer[Pointer+4]* 0x100) +lpFrameBuffer[Pointer+5]; int FragmentData = ((int) lpFrameBuffer[Pointer+6]* 0x100) + lpFrameBuffer[Pointer+7]; int TTL = (int) lpFrameBuffer[Pointer+8] & 0x00ff; int Protocol = (int) lpFrameBuffer[Pointer+9] & 0x00ff; int CheckSum = ((int) lpFrameBuffer[Pointer+10]* 0x100) + lpFrameBuffer[Pointer+11]; OutPut("\n#=== IP HEADER"); sprintf(buffer,"\nIP : Version = [%2d]",Version); OutPut(buffer); sprintf(buffer,"\nIP : Current Header = [%4u]",HeaderLength); OutPut(buffer); sprintf(buffer,"\nIP : Type of Service = [%s. %s. %s.]", Service & 0x0010 ? "Low Delay" : "Normal Delay", Service & 0x0008 ? "High Throughput" : "Normal Troughput", Service & 0x0004 ? "High Reliability" : "Normal Reliability"); OutPut(buffer); sprintf(buffer,"\nIP : Total Length = [%4u]",TotalLength); OutPut(buffer); sprintf(buffer,"\nIP : Identification = [0x%04x]",Identification); OutPut(buffer); sprintf(buffer,"\nIP : Fragment = [0x%04x]",FragmentData); OutPut(buffer); sprintf(buffer,"\nIP : TTL = [%4u]",TTL); OutPut(buffer); *ProtType = Protocol; if( Protocol == 6 ) { sprintf(buffer,"\nIP : Protocol = TCP "); } else if ( Protocol == 17 ) { sprintf(buffer,"\nIP : Protocol = UDP "); } else if (Protocol == 1 ) { sprintf(buffer,"\nIP : Protocol = ICMP "); } else { sprintf(buffer,"\nIP : Protocol = %d ",Protocol); sprintf(szProtocolName,"IP Packet"); } OutPut(buffer); sprintf(buffer,"\nIP : Check Sum = [0x%04x] ",CheckSum); OutPut(buffer); sprintf(buffer,"\nIP : Src IP Address = [%03u.%03u.%03u.%03u]", lpFrameBuffer[Pointer+12] & 0x00ff, lpFrameBuffer[Pointer+13] & 0x00ff, lpFrameBuffer[Pointer+14] & 0x00ff, lpFrameBuffer[Pointer+15] & 0x00ff); OutPut(buffer); sprintf(buffer,"\nIP : Dst IP Address = [%03u.%03u.%03u.%03u]", lpFrameBuffer[Pointer+16] & 0x00ff, lpFrameBuffer[Pointer+17] & 0x00ff, lpFrameBuffer[Pointer+18] & 0x00ff, lpFrameBuffer[Pointer+19] & 0x00ff); OutPut(buffer); if( HeaderLength > 20 ) { int i; char wk[10]; sprintf(buffer,"\nIP : Option = "); for(i = Pointer+20; i < Pointer + HeaderLength;i++ ) { sprintf(wk,"%02x",(int)lpFrameBuffer[i] & 0x00ff); strcat(buffer,wk); } OutPut(buffer); } return(Pointer+HeaderLength); } int ICMPHeader(char far *lpFrameBuffer,int Pointer,int FrameLength,FILE *OutFp,int *MsgType) { unsigned int Type = (unsigned int)lpFrameBuffer[Pointer]; unsigned int MsgCode = (unsigned int ) lpFrameBuffer[Pointer+1]; unsigned int CheckSum = (unsigned int)lpFrameBuffer[Pointer+2]*0x100 + (unsigned int)lpFrameBuffer[Pointer+3]; OutPut("\n#=== ICMP Header"); switch (Type ) { case 0: // Echo Reply case 8: // Echo { unsigned int ID = (unsigned int)lpFrameBuffer[Pointer+4]*0x100+ (unsigned int)lpFrameBuffer[Pointer+5]; unsigned int SEQ = (unsigned int)lpFrameBuffer[Pointer+6]*0x100+ (unsigned int)lpFrameBuffer[Pointer+7]; if( Type ) { sprintf(szProtocolName,"ICMP Echo"); sprintf(buffer,"\nICMP: Type = Echo Request"); } else { sprintf(szProtocolName,"ICMP Echo Reply"); sprintf(buffer,"\nICMP: Type = Echo Reply"); } OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%u] ID = [%04x] Seq = [%u]",MsgCode,ID,SEQ); OutPut(buffer); } return(Pointer+8); case 3: // Desttination Unreachable sprintf(buffer,"\nICMP: Type = Destination Unreachable"); OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%d]",MsgCode); OutPut(buffer); sprintf(szProtocolName,"ICMP Destination Unreachable"); break; case 4: // Source Quench sprintf(buffer,"\nICMP: Type = Source Quench"); OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%d]",MsgCode); OutPut(buffer); sprintf(szProtocolName,"ICMP Source Quench"); break; case 5: // Redirect sprintf(buffer,"\nICMP: Type = Redirect"); OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%d]",MsgCode); OutPut(buffer); sprintf(szProtocolName,"ICMP Redirect"); break; case 11: // Time Exceeded Message sprintf(buffer,"\nICMP: Type = Time Exceeded"); OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%d]",MsgCode); OutPut(buffer); sprintf(szProtocolName,"ICMP Time Exceeded"); break; case 12: //Parameter Problem sprintf(buffer,"\nICMP: Type = Parameter Problem"); OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%d]",MsgCode); OutPut(buffer); sprintf(szProtocolName,"ICMP Parameter Problem"); break; case 13: //Time Stamp sprintf(buffer,"\nICMP: Type = Time Stamp Request"); OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%d]",MsgCode); OutPut(buffer); sprintf(szProtocolName,"ICMP Time Stamp Request"); break; case 14: //Time Stamp Reply sprintf(buffer,"\nICMP: Type = Time Stamp Reply"); OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%d]",MsgCode); OutPut(buffer); sprintf(szProtocolName,"ICMP Time Stamp Reply"); break; case 15: //Information Request case 16: //Information Reply { unsigned int ID = (unsigned int)lpFrameBuffer[Pointer+4]*0x100+ (unsigned int)lpFrameBuffer[Pointer+5]; unsigned int SEQ = (unsigned int)lpFrameBuffer[Pointer+6]*0x100+ (unsigned int)lpFrameBuffer[Pointer+7]; if( Type == 15 ) { sprintf(buffer,"\nICMP: Type = Information Request"); } else { sprintf(buffer,"\nICMP: Type = Information Reply"); } OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%u] ID = [%04x] Seq = [%u]",MsgCode,ID,SEQ); OutPut(buffer); } sprintf(szProtocolName,"ICMP Information"); return(Pointer+8); case 17: //Address Mask Request case 18: //Address Mask Reply { unsigned int ID = (unsigned int)lpFrameBuffer[Pointer+4]*0x100+ (unsigned int)lpFrameBuffer[Pointer+5]; unsigned int SEQ = (unsigned int)lpFrameBuffer[Pointer+6]*0x100+ (unsigned int)lpFrameBuffer[Pointer+7]; if( Type ) { sprintf(buffer,"\nICMP: Type = Address Mask Request"); } else { sprintf(buffer,"\nICMP: Type = Address Mask Reply"); } OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%u] ID = [%04x] Seq = [%u]",MsgCode,ID,SEQ); OutPut(buffer); sprintf(buffer,"\nICMP: Mask = [%03u.%03u.%03u.%03u]", lpFrameBuffer[Pointer+8] & 0x00ff, lpFrameBuffer[Pointer+9] & 0x00ff, lpFrameBuffer[Pointer+10] & 0x00ff, lpFrameBuffer[Pointer+11] & 0x00ff); OutPut(buffer); } sprintf(szProtocolName,"ICMP Address Mask"); return(Pointer+12); default: sprintf(buffer,"\nICMP: Type = Unkwon"); OutPut(buffer); sprintf(buffer,"\nICMP: Code =[%d]",MsgCode); OutPut(buffer); sprintf(szProtocolName,"ICMP Unkwon"); } return(Pointer+4); } char * GetProtName(int sp,int dp); int UDPHeader(char far *lpFrameBuffer,int Pointer,int FrameLength,FILE *OutFp,int *ProtocolType) { int HeaderLength = 8; int SourcePort = ((int)lpFrameBuffer[Pointer] * 0x100 ) + ((int)lpFrameBuffer[Pointer+1] & 0x00ff) ; int DestPort = ((int)lpFrameBuffer[Pointer+2]* 0x100 ) + ((int)lpFrameBuffer[Pointer+3] & 0x00ff) ; int UDPDataLength = ((int) lpFrameBuffer[Pointer+4] *0x100 ) + ((int ) lpFrameBuffer[Pointer+5] & 0x00ff); int UDPCheckSum = ((int) lpFrameBuffer[Pointer+6] *0x100 ) + ((int ) lpFrameBuffer[Pointer+7] & 0x00ff); OutPut("\n#=== UDP Header"); sprintf(buffer,"\nUDP: Src Port = [%5u]",SourcePort); OutPut(buffer); sprintf(buffer,"\nUDP: Dst Port = [%5u]",DestPort); OutPut(buffer); sprintf(buffer,"\nUDP: Length = [%4u]",UDPDataLength); OutPut(buffer); sprintf(buffer,"\nUDP: CheckSum = [0x%04x]",UDPCheckSum); OutPut(buffer); sprintf(szProtocolName,GetProtName(SourcePort,DestPort)); return( Pointer+HeaderLength); } struct _PortList { int Port; char szName[50]; } PortList[] = { -1, "Unkwon", 5, "Remote Job Entry", 7, "Echo", 9, "Discard", 11, "Active Users", 13, "Daytime", 15, "Unassigned", 17, "Quote of the Day", 19, "Character Generator", 20, "File Transfer [Default Data]", 21, "FTP File Transfer [Control]", 23, "Telnet", 25, "Simple Mail Transfer", 37, "Time", 42, "Host Name Server", 43, "Who Is", 49, "Login Host Protocol", 53, "Domain Name Server", 67, "Bootstrap Protocol Server", 68, "Bootstrap Protocol Client", 69, "Trivial File Transfer", 109,"Post Office Protocol - Ver 2", 110,"Post Office Protocol - Ver 3", 111,"SUN Remote Procedure Call", 119,"Network News Transfer", 123,"Network Time Protocol", 137,"NETBIOS Name Service", 138,"NETBIOS Datagram Service", 139,"NETBIOS Session Service", 153,"SGMP", 160,"SGMP-TRAPS", 161,"SNMP", 162,"SNMPTRAP", 0,"" }; char * GetProtName(int sp,int dp) { int i; for(i = 0; PortList[i].Port;i++ ) { if( sp == PortList[i].Port ) return(PortList[i].szName); if( dp == PortList[i].Port ) return(PortList[i].szName); } return(PortList[0].szName); } // TCPヘッダを出力する。 int TCPHeader(char far *lpFrameBuffer,int Pointer,int FrameLength,FILE *OutFp,int *ProtocolType) { int SourcePort = ((int)lpFrameBuffer[Pointer] * 0x100 ) + ((int)lpFrameBuffer[Pointer+1] & 0x00ff) ; int DestPort = ((int)lpFrameBuffer[Pointer+2]* 0x100 ) + ((int)lpFrameBuffer[Pointer+3] & 0x00ff) ; unsigned long SEQNumber = ((unsigned long) lpFrameBuffer[Pointer+4] * 0x01000000L) + ((unsigned long) lpFrameBuffer[Pointer+5] * 0x00010000L) + ((unsigned long) lpFrameBuffer[Pointer+6] * 0x00000100L) + ((unsigned long) lpFrameBuffer[Pointer+7] ); unsigned long ACKNumber = ((unsigned long) lpFrameBuffer[Pointer+8] * 0x01000000L) + ((unsigned long) lpFrameBuffer[Pointer+9] * 0x00010000L) + ((unsigned long) lpFrameBuffer[Pointer+10] * 0x00000100L) + ((unsigned long) lpFrameBuffer[Pointer+11] ); int DataOffset; int ControlFlag = (int) lpFrameBuffer[Pointer+13] & 0x003f; int Window = ((int) lpFrameBuffer[Pointer+14] * 0x100) + ( (int) lpFrameBuffer[Pointer+15] ); int TCPCheckSum = ((int) lpFrameBuffer[Pointer+16] * 0x100) + ( (int) lpFrameBuffer[Pointer+17] ); int UrgentPointer = ((int) lpFrameBuffer[Pointer+18] * 0x100) + ( (int) lpFrameBuffer[Pointer+19] ); int HeaderLength = (int)lpFrameBuffer[Pointer+12]; HeaderLength >>= 2; HeaderLength &= 0x3e; DataOffset = HeaderLength; OutPut("\n#=== TCP Header"); sprintf(buffer,"\nTCP: Src Port = [%5u]",SourcePort); OutPut(buffer); sprintf(buffer,"\nTCP: Dst Port = [%5u]",DestPort); OutPut(buffer); sprintf(buffer,"\nTCP: SEQ Number = [%12lu]",SEQNumber); OutPut(buffer); sprintf(buffer,"\nTCP: ACK Number = [%12lu]",ACKNumber); OutPut(buffer); sprintf(buffer,"\nTCP: Data Offset = [%4u]",DataOffset); OutPut(buffer); sprintf(buffer,"\nTCP: Control Flags = [%s %s %s %s %s %s]", ControlFlag & 0x0020 ? "URG" : "", ControlFlag & 0x0010 ? "ACK" : "", ControlFlag & 0x0008 ? "PSH" : "", ControlFlag & 0x0004 ? "RST" : "", ControlFlag & 0x0002 ? "SYN" : "", ControlFlag & 0x0001 ? "FIN" : ""); OutPut(buffer); sprintf(buffer,"\nTCP: Window = [%4u] ",Window); OutPut(buffer); sprintf(buffer,"\nTCP: Check Sum = [0x%04x] ",TCPCheckSum); OutPut(buffer); sprintf(buffer,"\nTCP: Urgent Pointer = [%4u] ",UrgentPointer); OutPut(buffer); if( HeaderLength > 20 ) { int i; char wk[10]; sprintf(buffer,"\nTCP: Option = "); for(i = Pointer+20; i < Pointer + HeaderLength;i++ ) { sprintf(wk,"%02x",(int)lpFrameBuffer[i] & 0x00ff); strcat(buffer,wk); } OutPut(buffer); } sprintf(szProtocolName,GetProtName(SourcePort,DestPort)); return( Pointer+HeaderLength); } void DumpPacket(char *pPacket, int paketLen) { int EtherType; int ProtType; char *lpFrameBuffer; int j; int len; lpFrameBuffer = pPacket; len = paketLen; sprintf(szProtocolName,""); j = 0; j = EtherHeader(lpFrameBuffer,0,len,stdout,&EtherType); if( EtherType == SNAP_PKT_TYPE ) { j = SNAPHeader(lpFrameBuffer,j,len,stdout,&EtherType); } if( EtherType == ARP_PKT_TYPE ) { j = ARPHeader(lpFrameBuffer,j,len,stdout); sprintf(szProtocolName,"ARP"); } else if ( EtherType == IP_PKT_TYPE ) { j = IPHeader(lpFrameBuffer,j,len,stdout,&ProtType); if( ProtType == TCP_PKT_TYPE ) { j = TCPHeader(lpFrameBuffer,j,len,stdout,&ProtType); } else if ( ProtType == UDP_PKT_TYPE ) { j = UDPHeader(lpFrameBuffer,j,len,stdout,&ProtType); } else if ( ProtType == ICMP_PKT_TYPE ) { j = ICMPHeader(lpFrameBuffer,j,len,stdout,&ProtType); } } else { sprintf(szProtocolName,"Other"); } }