c - 使用 UDP 传输格式化 SOCK_RAW 以查询 NTP
问题描述
我想使用专门的原始套接字查询 NTP 服务器。这是我一段时间后得到的,但我的回复被解码为 12/31/1899。我认为我的结构是正确的,但我搞砸了我传递记忆的方式。(我在 Windows 上的 Ubuntu VM 中运行它。我确实在 VM 中以 root 身份运行该程序)。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#define LI(packet) (uint8_t) ((packet.li_vn_mode & 0xC0) >> 6)
#define VN(packet) (uint8_t) ((packet.li_vn_mode & 0x38) >> 3)
#define MODE(packet) (uint8_t) ((packet.li_vn_mode & 0x07) >> 0)
void goFail( char* msg ) {
perror(msg);
exit(1);
}
unsigned short checkSum(unsigned short *buf, int nwords) {
unsigned long sum;
for(sum=0; nwords>0; nwords--) {
sum += *buf++;
}
sum = (sum >> 16) + (sum &0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
struct ipheader {
unsigned char iph_ihl:5, iph_ver:4;
unsigned char iph_tos;
unsigned short int iph_len;
unsigned short int iph_ident;
unsigned char iph_flag;
unsigned short int iph_offset;
unsigned char iph_ttl;
unsigned char iph_protocol;
unsigned short int iph_chksum;
unsigned int iph_sourceip;
unsigned int iph_destip;
};
struct udpheader {
unsigned short int udph_srcport;
unsigned short int udph_destport;
unsigned short int udph_len;
unsigned short int udph_chksum;
};
int main() {
printf("\nStarted...");
fflush(stdout);
char* host_name = "pool.ntp.org";
char* source_name = "174.44.196.123";
typedef struct {
uint8_t li_vn_mode;
uint8_t stratum;
uint8_t poll;
uint8_t precision;
uint32_t rootDelay;
uint32_t rootDispersion;
uint32_t refId;
uint32_t refTm_s;
uint32_t refTm_f;
uint32_t origTm_s;
uint32_t origTm_f;
uint32_t rxTm_s;
uint32_t rxTm_f;
uint32_t txTm_s;
uint32_t txTm_f;
} ntp_packet;
// ntp_packet ntp_data_packet;// = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// memset( &ntp_data_packet, 0, sizeof( ntp_packet ) );
// *( ( char * ) &ntp_data_packet + 0 ) = 0x1b;
int raw_sock;
//TODO what buffer size??
char buffer[sizeof(ntp_packet) + sizeof(struct ipheader) + sizeof(struct udpheader)];
memset(&buffer, 0, sizeof(buffer));
struct ipheader *ip = (struct ipheader *) &buffer[0];
struct udpheader *udp = (struct udpheader *) (&buffer[0] + sizeof(struct ipheader));
ntp_packet *ntp_data_packet = (ntp_packet*)&buffer + sizeof (struct ipheader) + sizeof(struct udpheader);
printf("\nsizeof ip %d",sizeof(*ip));
printf("\nsizeof ud %d",sizeof(*udp));
printf("\nsizeof nt %d",sizeof(*ntp_data_packet));
printf("\nsizeof buf %d\n",sizeof(buffer));
ntp_data_packet->li_vn_mode = 0x1b;
struct sockaddr_in sin, din;
int one = 1;
const int *val = &one;
//TODO another size
// memset(&buffer, 0, sizeof (sizeof(ntp_packet)));
// Create a raw socket with UDP protocol
raw_sock = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
if(raw_sock < 0) {
goFail("Socket() error");
}
sin.sin_family = AF_INET;
din.sin_family = AF_INET;
sin.sin_port = htons(613);
din.sin_port = htons(123);
sin.sin_addr.s_addr = inet_addr(source_name);
din.sin_addr.s_addr = inet_addr(host_name);
ip->iph_ihl = 5;
ip->iph_ver = 4;
ip->iph_tos = 16; // Low delay
ip->iph_len = sizeof(struct ipheader) + sizeof(struct udpheader) +sizeof(ntp_packet) ;
ip->iph_ident = htons(6130);
ip->iph_ttl = 255; // hops
ip->iph_protocol = 17; // UDP
ip->iph_sourceip = inet_addr(source_name);
ip->iph_destip = inet_addr(gethostbyname(host_name));
udp->udph_srcport = htons(613);
udp->udph_destport = htons(123);
udp->udph_len = htons(sizeof(struct udpheader) + sizeof(ntp_packet));
ip->iph_chksum = checkSum((unsigned short *)&ntp_data_packet, sizeof(struct ipheader) + sizeof(struct udpheader));
// if(setsockopt(raw_sock, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) {
// goFail("setSockOpt() error");
// }
printf("...Using raw socket and UDP protocol...");
// int wrresponse = write( sd, ( char* ) &packet, sizeof( ntp_packet ) );
// int read_response = read( sd, ( char* ) &packet, sizeof( ntp_packet ) );
printf("\nBefore send\n");
for(int i =0; i<sizeof (buffer);i++) {
if (i%16==0) {
printf("\n");
}
printf("%x ",buffer[i] & 0xff);
}
if( sendto(raw_sock, &buffer, sizeof(buffer), 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
goFail("sendto() error");
}
printf("\nBefore read: %d",ntp_data_packet->txTm_s);
printf("...Sendto() is OK...");
// int n = read( raw_sock, ( char* ) buffer, sizeof(buffer) );
int n = recv(raw_sock,buffer,sizeof(buffer),0);
printf("\nAfter read with result %d\n",n);
for(int i =0; i<sizeof (buffer);i++) {
if (i%16==0) {
printf("\n");
}
printf("%x ",buffer[i] & 0xff);
}
printf("...Got a packet!...");
fflush(stdout);
if ( n <= 0 ) {
goFail( "couldnt read from socket" );
}
printf("\nAfter read: %d\n",ntp_data_packet->txTm_s);
ntp_data_packet->txTm_s = ntohl( ntp_data_packet->txTm_s );
ntp_data_packet->txTm_f = ntohl( ntp_data_packet->txTm_f );
printf("\nAfter format: %d\n",ntp_data_packet->txTm_s);
time_t txTm = ( time_t ) ( ntp_data_packet->txTm_s - 2208988800ull );
printf( "DECODED RAW: The time is: %s", ctime( ( const time_t* ) &txTm ) );
close(raw_sock);
return 0;
}
(我知道我可以只使用 SOCK_DGRAM,但这个练习是探索原始套接字)。
输出
Program output:
Started...
sizeof ip 24
sizeof ud 8
sizeof nt 48
sizeof buf 80
...Using raw socket and UDP protocol...
Before send
5 4 10 0 50 0 17 f2 0 0 0 0 ff 11 a6 d5
ae 2c c4 7b ff ff ff ff 2 65 0 7b 0 38 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Before read: 3681606...Sendto() is OK...
After read with result 69
45 0 0 45 4a 5f 40 0 40 11 f2 12 7f 0 0 1
7f 0 0 35 94 ec 0 35 0 31 fe 78 6d 6 1 0
0 1 0 0 0 0 0 1 4 70 6f 6f 6c 3 6e 74
70 3 6f 72 67 0 0 1 0 1 0 0 29 4 b0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...Got a packet!...
After read: 3681606
After format: 1177368576
DECODED RAW: The time is: Fri Apr 23 17:49:36 1937
解决方案
推荐阅读
- html - 如何使“名字”项目及其形式在同一行中
- python - 如何可视化 H2O 树?
- azure-devops - 使用 YAML Azure Pipelines 时是否可以手动触发阶段?
- sql - Crate DB - 使用 COPY TO 命令将表导出到不同的 S3 端点
- c++ - 在 Windows 命令行中运行 .exe 会在以 2 种相同方式执行时产生不同的输出
- java - 多线程中的 MQ 与 REST 性能
- python - 如果数据低于熊猫数据框中的阈值,则获取布尔值
- sql - Sum sql问题 - 里面的两列是连接的
- go - 解码base64并将其编码为十六进制
- macos - 将“无线”显示为标题的命令行