#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <time.h>

// Ensure 64-bit integer byte-order functions
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#include <byteswap.h>
#define htobe64(x) __bswap_64(x)
#else
#define htobe64(x) (x)
#endif

// Define NTP packet structure
#pragma pack(push, 1)
struct ntp_packet {
    uint8_t li_vn_mode;
    uint8_t stratum;
    int8_t poll;
    int8_t precision;

    uint32_t root_delay;
    uint32_t root_dispersion;
    uint32_t reference_id;

    uint64_t ref_timestamp;
    uint64_t orig_timestamp;
    uint64_t recv_timestamp;
    uint64_t transmit_timestamp;
};
#pragma pack(pop)

// Create a UDP socket with broadcast enabled
int create_broadcast_socket() {
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("Socket creation failed");
        return -1;
    }

    int broadcastEnable = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable)) < 0) {
        perror("Enable broadcast failed");
        close(sock);
        return -1;
    }

    return sock;
}

// Fill packet with Mode 5 (broadcast) NTP data
void fill_ntp_broadcast_packet(struct ntp_packet* pkt) {
    memset(pkt, 0, sizeof(struct ntp_packet));

    pkt->li_vn_mode = (0 << 6) | (4 << 3) | 5; // LI=0, VN=4, Mode=5 (broadcast)
    pkt->stratum = 1;     // Primary server
    pkt->poll = 4;        // 16s interval
    pkt->precision = -6;  // ~15.6ms

    pkt->root_delay = htonl(1 << 16);        // 1.0 in NTP short format
    pkt->root_dispersion = htonl(1 << 16);   // 1.0
    pkt->reference_id = htonl(0x4C4F434C);   // 'LOCL'

    time_t now = time(NULL) + 2208988800U;   // UNIX to NTP epoch (1900)
    uint64_t ntp_time = ((uint64_t)now) << 32;

    pkt->ref_timestamp = htobe64(ntp_time);
    pkt->transmit_timestamp = htobe64(ntp_time);
}

// Send the NTP broadcast message to the provided broadcast IP
void send_ntp_broadcast(const char* broadcast_ip) {
    int sock = create_broadcast_socket();
    if (sock < 0) return;

    struct sockaddr_in dest = {0};
    dest.sin_family = AF_INET;
    dest.sin_port = htons(123);
    inet_pton(AF_INET, broadcast_ip, &dest.sin_addr);

    struct ntp_packet pkt;
    fill_ntp_broadcast_packet(&pkt);

    ssize_t sent = sendto(sock, &pkt, sizeof(pkt), 0,
                          (struct sockaddr*)&dest, sizeof(dest));
    if (sent < 0) {
        perror("Send failed");
    } else {
        printf("Broadcast NTP packet sent to %s (%ld bytes)\n", broadcast_ip, sent);
    }

    close(sock);
}

// Main function to handle command line argument
int main(int argc, char* argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <broadcast_ip>\n", argv[0]);
        return 1;
    }

    send_ntp_broadcast(argv[1]);
    return 0;
}

