4 * Copyright (C) 2008 Dave Hansen <dave@sr71.net>
6 * This software may be redistributed and/or modified under the terms of
7 * the GNU General Public License ("GPL") version 2 as published by the
8 * Free Software Foundation.
17 #include <sys/types.h>
22 #include "eyefi-config.h"
24 #define O_DIRECT 00040000 /* direct disk access hint */
33 #define PATHNAME_MAX 4096
34 char eyefi_mount[PATHNAME_MAX]; // PATH_MAX anyone?
35 static char *__eyefi_file(enum eyefi_file file)
38 case REQC: return "reqc";
39 case REQM: return "reqm";
40 case RSPC: return "rspc";
41 case RSPM: return "rspm";
47 static char *eyefi_file(enum eyefi_file file)
49 char *filename = __eyefi_file(file);
50 char *full = malloc(PATHNAME_MAX);
52 sprintf(&full[0], "%s/EyeFi/%s", eyefi_mount, filename);
58 #define EYEFI_BUF_SIZE 16384
59 char unaligned_buf[BUFSZ*2];
64 #define debug_printf(level, args...) do { \
65 if ((level) <= debug_level) \
66 fprintf(stderr, ## args); \
70 * Just a few functions so that I can't easily forget about
75 } __attribute__((packed));
76 typedef struct __be32 be32;
79 * These two obviously need to get fixed for
80 * big endian machines.
82 u32 be32_to_u32(be32 src)
84 return swap_bytes(src.val);
86 be32 u32_to_be32(u32 src)
89 ret.val = swap_bytes(src);
93 void dumpbuf(const char *buffer, int bytesToWrite)
96 static char linebuf[500];
98 for (i=0; i < bytesToWrite; i += 16) {
99 char *tmpbuf = &linebuf[0];
100 unsigned long sum = 0;
102 #define lprintf(args...) do { \
103 tmpbuf += sprintf(tmpbuf, ## args);\
106 lprintf("[%03d]: ", i);
107 for (j=0; j < 16; j++) {
108 u8 c = ((unsigned char *)buffer)[i+j];
109 lprintf("%02x ", (unsigned int)c);
113 for (j=0; j < 16; j++) {
114 u8 c = ((unsigned char *)buffer)[i+j];
115 if (c >= 'a' && c <= 'z')
117 else if (c >= 'A' && c <= 'Z')
119 else if (c >= '0' && c <= '9')
121 else if (c >= 0x20 && c <= 127)
129 printf("%s", linebuf);
135 struct card_seq_num {
137 } __attribute__((packed));
139 void read_from(enum eyefi_file);
140 void write_to(enum eyefi_file, void *, int);
141 struct card_seq_num read_seq_from(enum eyefi_file file)
143 struct card_seq_num *ret;
150 * For O_DIRECT writes to files, we need
151 * to be 512 byte aligned on Linux, I think.
152 * So, just align this to something big
153 * and be done with it. FIXME :)
157 unsigned long addr = (unsigned long)&unaligned_buf[BUFSZ];
160 debug_printf(4, "buf: %p\n", buf);
161 debug_printf(4, "unaligned: %p\n", &unaligned_buf[0]);
164 struct card_seq_num seq;
167 * The real manager does this so we might
170 void zero_card_files(void)
172 write_to(REQM, buf, BUFSZ);
173 write_to(REQC, buf, BUFSZ);
174 write_to(RSPM, buf, BUFSZ);
175 write_to(RSPC, buf, BUFSZ);
188 debug_printf(2, "Initializing card...\n");
191 seq = read_seq_from(RSPC);
194 debug_printf(2, "Done initializing card...\n");
197 void open_error(char *file)
199 fprintf(stderr, "unable to open '%s'\n", file);
200 fprintf(stderr, "Is the Eye-Fi card inserted and mounted at: %s ?\n", eyefi_mount);
201 fprintf(stderr, "Do you have write permissions to it?\n");
207 void read_from(enum eyefi_file __file)
214 char *file = eyefi_file(__file);
218 fd = open(file, O_RDONLY);
221 retcntl = fcntl(fd, F_SETFL, O_DIRECT);
226 ret = read(fd, buf, BUFSZ);
233 debug_printf(3, "read '%s': bytes: %d fcntl: %d\n", file, ret, retcntl);
234 for (i=0; i < BUFSZ; i++) {
235 c = ((char *)buf)[i];
242 // printf(" zeros: %d", zeros);
248 void write_to(enum eyefi_file __file, void *stuff, int len)
252 char *file = eyefi_file(__file);
258 if (debug_level > 3) {
259 debug_printf(3, "%s('%s', ..., %d)\n", __func__, file, len);
262 memset(buf, 0, BUFSZ);
263 memcpy(buf, stuff, len);
264 fd = open(file, O_RDWR|O_DIRECT|O_CREAT, 0600);
265 //ret = lseek(fd, 0, SEEK_SET);
270 ret = write(fd, buf, BUFSZ);
273 debug_printf(3, "wrote %d bytes to '%s' (string was %d bytes)\n", ret, file, len);
280 * Most of the eyefi strings are pascal-style with
281 * a length byte preceeding content. (Did pascal
282 * have just a byte for length or more??)
284 struct pascal_string {
287 } __attribute__((packed));
289 void print_pascal_string(struct pascal_string *str)
292 for (i = 0; i < str->length; i++)
293 printf("%c", str->value[i]);
297 * The 'o' command has several sub-commands:
299 enum card_info_subcommand {
304 UNKNOWN1 = 5, // Chris says these are
305 UNKNOWN2 = 6, // checksums
309 struct card_info_req {
312 } __attribute__((packed));
314 struct card_info_rsp_key {
315 struct pascal_string key;
322 } __attribute__((packed));
324 struct card_info_api_url {
325 struct pascal_string key;
328 struct card_info_log_len {
331 } __attribute__((packed));
333 #define write_struct(file, s) write_to((file), s, sizeof(*(s)))
335 void print_mac(struct mac_address *mac)
338 for (i=0; i < MAC_BYTES-1; i++) {
339 printf("%02x:", mac->mac[i]);
341 printf("%02x\n", mac->mac[i]);
346 //u32 tmpseq = be32_to_u32(seq.seq);
347 //seq.seq = u32_to_be32(tmpseq+1);
349 write_struct(REQC, &seq);
352 u32 current_seq(void)
357 void wait_for_response(void)
360 debug_printf(3, "waiting for response...\n");
362 for (i = 0; i < 50; i++) {
363 struct card_seq_num cardseq = read_seq_from(RSPC);
364 u32 rsp = cardseq.seq;
365 debug_printf(3, "read rsp code: %lx, looking for: %lx raw: %lx\n", rsp, current_seq(),
367 if (rsp == current_seq())
371 debug_printf(3, "got good seq, reading RSPM...\n");
373 debug_printf(3, "done reading RSPM\n");
375 struct byte_response {
388 char essid[ESSID_LEN];
389 signed char strength;
391 } __attribute__((packed));
393 struct scanned_net_list {
395 struct scanned_net nets[100];
396 } __attribute__((packed));
398 struct configured_net {
399 char essid[ESSID_LEN];
400 } __attribute__((packed));
402 struct configured_net_list {
404 struct configured_net nets[100];
405 } __attribute__((packed));
407 char *net_test_states[] = {
410 "verifying network key",
412 "testing connection to Eye-Fi server",
416 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
418 char *net_test_state_name(u8 state)
420 int size = ARRAY_SIZE(net_test_states);
423 return net_test_states[state];
426 char *net_types[] = {
434 char *net_type_name(u8 type)
436 int size = ARRAY_SIZE(net_types);
439 return net_types[type];
442 #define WPA_KEY_BYTES 32
444 u8 key[WPA_KEY_BYTES];
445 } __attribute((packed));
447 #define WEP_KEY_BYTES 32
449 u8 key[WEP_KEY_BYTES];
450 } __attribute((packed));
458 } __attribute((packed));
464 char essid[ESSID_LEN];
465 struct network_key key;
466 } __attribute((packed));
470 if ((c >= 'A') && (c <= 'Z'))
478 if ((c >= '0') && (c <= '9'))
480 else if ((c >= 'a') && (c <= 'z'))
481 return (c - 'a') + 10;
482 debug_printf(5, "non-hex character: '%c'/'%c'\n", c, lc);
487 * Take a string like "0ab1" and make it
488 * a series of bytes: { 0x0a, 0xb1 }
490 * @len is the strlen() of the ascii
492 * Destroys the original string.
494 char *convert_ascii_to_hex(char *ascii, int len)
498 fprintf(stderr, "%s() must be even number of bytes: %d\n",
502 for (i=0; i < len; i+=2) {
503 int high = atoh(ascii[i]);
504 int low = atoh(ascii[i+1]);
505 u8 byte = (high<<4 | low);
506 if (high < 0 || low < 0)
508 debug_printf(6, "high: %02x low: %02x, both: %02x\n", high, low, byte);
511 for (i=len/2; i < len; i++)
516 #define PASSPHRASE_PROG "wpa_passphrase"
518 struct wpa_key *make_wpa_key(char *essid, char *pass)
520 struct wpa_key *key = malloc(sizeof(*key));
522 if (strlen(pass) == WPA_KEY_BYTES*2) {
524 debug_printf(2, "Interpreting password as hex WPA key\n");
525 hex_pass = convert_ascii_to_hex(pass, WPA_KEY_BYTES*2);
528 memcpy(&key->key[0], pass, WPA_KEY_BYTES);
530 debug_printf(2, "Interpreting password as ASCII WPA key\n");
531 pbkdf2_sha1(pass, essid, strlen(essid), 4096,
532 &key->key[0], WPA_KEY_BYTES);
537 void card_info_cmd(enum card_info_subcommand cmd)
539 struct card_info_req cir;
541 cir.subcommand = cmd;
543 write_struct(REQM, &cir);
547 u32 fetch_log_length(void)
549 card_info_cmd(LOG_LEN);
550 struct card_info_log_len *loglen = buf;
551 return be32_to_u32(loglen->val);
554 void print_log_len(void)
556 u32 len = fetch_log_length();
557 printf("log len: %08lx\n", len);
560 void print_card_mac(void)
562 debug_printf(2, "%s()\n", __func__);
563 card_info_cmd(MAC_ADDRESS);
564 struct mac_address *mac = buf;
565 assert(mac->length == MAC_BYTES);
566 printf("card mac address: ");
570 void print_card_key(void)
572 debug_printf(2, "%s()\n", __func__);
573 card_info_cmd(CARD_KEY);
574 struct card_info_rsp_key *foo = buf;
575 printf("card key (len: %d): '", foo->key.length);
576 print_pascal_string(&foo->key);
580 struct noarg_request {
584 void issue_noarg_command(u8 cmd)
586 struct noarg_request req;
588 write_struct(REQM, &req);
592 void scan_print_nets(void)
596 debug_printf(2, "%s()\n", __func__);
597 issue_noarg_command('g');
598 struct scanned_net_list *scanned = buf;
599 if (scanned->nr == 0) {
600 printf("unable to detect any wireless networks\n");
603 printf("Scanned wireless networks:\n");
604 for (i=0; i < scanned->nr; i++) {
605 struct scanned_net *net = &scanned->nets[i];
606 printf("'%s' type(%d): %s, strength: %d\n", net->essid,
608 net_type_name(net->type),
613 void print_configured_nets(void)
616 struct configured_net_list *configured;
618 debug_printf(2, "%s()\n", __func__);
619 issue_noarg_command('l');
621 if (configured->nr == 0) {
622 printf("No wireless networks configured on card\n");
625 printf("configured wireless networks:\n");
626 for (i=0; i < configured->nr; i++) {
627 struct configured_net *net = &configured->nets[i];
628 printf("'%s'\n", net->essid);
632 void reboot_card(void)
634 debug_printf(2, "%s()\n", __func__);
635 issue_noarg_command('b');
638 void copy_wep_key(struct wep_key *dst, struct wep_key *src)
640 memcpy(&dst->key, &src->key, sizeof(*dst));
643 void copy_wpa_key(struct wpa_key *dst, struct wpa_key *src)
645 memcpy(&dst->key, &src->key, sizeof(*dst));
648 void network_action(char cmd, char *essid, char *wpa_ascii)
650 struct net_request nr;
651 memset(&nr, 0, sizeof(nr));
654 strcpy(&nr.essid[0], essid);
655 nr.essid_len = strlen(essid);
656 struct wpa_key *wpakey;
658 wpakey = make_wpa_key(essid, wpa_ascii);
659 nr.key.len = sizeof(*wpakey);
660 copy_wpa_key(&nr.key.wpa, wpakey);
662 write_struct(REQM, &nr);
666 void add_network(char *essid, char *wpa_ascii)
668 debug_printf(2, "%s()\n", __func__);
669 network_action('a', essid, wpa_ascii);
672 void remove_network(char *essid)
674 debug_printf(2, "%s()\n", __func__);
675 network_action('d', essid, NULL);
678 int try_connection_to(char *essid, char *wpa_ascii)
683 char *type = net_type_name(WPA);
685 type = net_type_name(UNSECURED);
686 printf("trying to connect to %s network: '%s'", type, essid);
688 printf(" with passphrase: '%s'", wpa_ascii);
692 network_action('t', essid, wpa_ascii);
696 for (i=0; i < 200; i++) {
697 struct byte_response *r;
698 issue_noarg_command('s');
701 char *state = net_test_state_name(rsp);
702 if (rsp == last_rsp) {
707 printf("\nTesting connecion to '%s' (%d): %s", essid, rsp, state);
711 if (!strcmp("success", state)) {
715 if (!strcmp("not scanning", state))
717 if (!strcmp("unknown", state))
722 printf("Succeeded connecting to: '%s'\n", essid);
724 printf("Unable to connect to: '%s' (final state: %d/'%s')\n", essid,
725 rsp, net_test_state_name(rsp));
730 struct fetch_log_cmd {
733 } __attribute__((packed));
736 * When you ask for the log at offset 0x0, you
737 * get back 8 bytes of offsets into the rest of
740 struct first_log_response {
743 u8 data[EYEFI_BUF_SIZE-8];
744 } __attribute__((packed));
746 struct rest_log_response {
747 u8 data[EYEFI_BUF_SIZE];
748 } __attribute__((packed));
750 unsigned char *get_log_at_offset(u32 offset)
752 struct fetch_log_cmd cmd;
754 cmd.offset = u32_to_be32(offset);
756 debug_printf(2, "getting log at offset: %08lx\n", offset);
757 write_struct(REQM, &cmd);
768 u32 log_size = fetch_log_length();
769 char *resbuf = malloc(log_size);
771 int nr_bufs_per_log = log_size/EYEFI_BUF_SIZE;
772 for (i = 0; i < log_size/EYEFI_BUF_SIZE; i++) {
773 debug_printf(1, "fetching EyeFi card log part %d/%d...",
774 i+1, nr_bufs_per_log);
776 get_log_at_offset(EYEFI_BUF_SIZE*i);
777 debug_printf(1, "done\n");
781 struct first_log_response *log = buf;
782 log_end = be32_to_u32(log->log_end);
783 log_start = be32_to_u32(log->log_start);
784 debug_printf(2, "log end: 0x%04lx\n", log_end);
785 debug_printf(2, "log start: 0x%04lx\n", log_start);
786 log_data = &log->data[0];
787 log_size = ARRAY_SIZE(log->data);
789 struct rest_log_response *log = buf;
790 log_data = &log->data[0];
791 log_size = ARRAY_SIZE(log->data);
793 debug_printf(3, "writing %ld bytes to resbuf[%d]\n",
794 log_size, total_bytes);
795 memcpy(&resbuf[total_bytes], log_data, log_size);
796 total_bytes += log_size;
798 // The last byte *should* be a null, and the
799 // official software does not print it.
800 for (i = 0; i < total_bytes-1; i++) {
801 int offset = (log_start+i)%total_bytes;
802 char c = resbuf[offset];
803 // the official software converts UNIX to DOS-style
804 // line breaks, so we'll do the same
810 // just some simple sanity checking to make sure what
811 // we are fetching looks valid
812 int null_bytes_left = 20;
813 if (resbuf[log_end] != 0) {
814 debug_printf(2, "error: unexpected last byte (%ld/0x%lx) of log: %02x\n",
815 log_end, log_end, resbuf[log_end]);
816 for (i=0; i<log_size; i++) {
819 if (null_bytes_left <= 0)
822 debug_printf(2, "null byte %d\n", i);
831 if ((o >= '0') && (o <= '7'))
836 int octal_esc_to_chr(char *input) {
839 int len = strlen(input);
841 //intf("%s('%s')\n", __func__, input);
842 if (input[0] != '\\')
847 for (i=1; i < len ; i++) {
850 int tmp = atoo(input[i]);
851 //intf("tmp: %d\n", tmp);
860 char *replace_escapes(char *str)
864 debug_printf(4, "%s(%s)\n", __func__, str);
865 for (i=0; i < strlen(str); i++) {
866 int esc = octal_esc_to_chr(&str[i]);
872 str[output++] = str[i];
875 debug_printf(4, "'%s' %d\n", str, output);
879 #define LINEBUFSZ 1024
880 void locate_eyefi_mount(void)
882 char line[LINEBUFSZ];
883 FILE *mounts = fopen("/proc/mounts", "r");
891 while (fgets(&line[0], 1023, mounts)) {
893 read = sscanf(&line[0], "%s %s %s %s %d %d",
894 &dev[0], &mnt[0], &fs[0], &opt[0],
896 // only look at fat filesystems:
897 if (strcmp(fs, "msdos") && strcmp(fs, "vfat")) {
898 debug_printf(2, "fs at '%s' is not fat, skipping...\n", mnt);
901 // Linux's /proc/mounts has spaces like this \040
902 replace_escapes(&mnt[0]);
903 strcpy(&eyefi_mount[0], &mnt[0]);
904 char *file = eyefi_file(REQM);
905 debug_printf(2, "looking for EyeFi file here: '%s'\n", file);
909 statret = stat(file, &statbuf);
913 debug_printf(1, "located EyeFi card at: %s\n", eyefi_mount);
922 printf(" eyefitest [OPTIONS]\n");
923 printf(" -a ESSID add network (implies test unless --force)\n");
924 printf(" -t ESSID test network\n");
925 printf(" -p KEY set WPA key for add/test\n");
926 printf(" -r ESSID remove network\n");
927 printf(" -s scan for networks\n");
928 printf(" -c list configured networks\n");
929 printf(" -b reboot card\n");
930 printf(" -d level set debugging level (default: 1)\n");
931 printf(" -k print card unique key\n");
932 printf(" -l dump card log\n");
933 printf(" -m print card mac\n");
937 int main(int argc, char **argv)
942 debug_printf(3, "%s starting...\n", argv[0]);
944 locate_eyefi_mount();
946 //static int passed_wep = 0;
947 //static int passed_wpa = 0;
948 static int force = 0;
949 static struct option long_options[] = {
950 //{"wep", 'x', &passed_wep, 1},
951 //{"wpa", 'y', &passed_wpa, 1},
952 {"force", 0, &force, 1},
953 {"help", 'h', NULL, 1},
960 char network_action = 0;
961 debug_printf(3, "about to parse arguments\n");
962 while ((c = getopt_long_only(argc, argv, "a:bcd:klmp:r:st:",
963 &long_options[0], &option_index)) != -1) {
964 debug_printf(3, "argument: '%c' %d optarg: '%s'\n", c, c, optarg);
967 // was a long argument
979 print_configured_nets();
982 debug_level = atoi(optarg);
1005 debug_printf(3, "after arguments essid: '%s' passwd: '%s'\n", essid, passwd);
1006 if (network_action && essid) {
1008 switch (network_action) {
1010 ret = try_connection_to(essid, passwd);
1014 ret = try_connection_to(essid, passwd);
1016 debug_printf(1, "forced: skipping network test\n");
1019 printf("Error connecting to network '%s', not adding.\n", essid);
1020 printf("use --force to override\n");
1023 add_network(essid, passwd);
1026 remove_network(essid);