libsslを使った暗号化と復号のサンプル
ちょっと必要になったので、軽い気持ちで書いてみました。
とりあえず手軽なところでAESとblowfishのサンプルです。
比較的簡単に書けるのでいろんなとこで使えそう。
こんな便利なライブラリを書いてくれた方々の偉大さを噛みしめながら利用したいと思います。
test.c
#include<stdio.h> #include<stdint.h> #include<string.h> #include<openssl/md5.h> #include<openssl/aes.h> #include<openssl/blowfish.h> char *encpass = "hoge"; void md5sum(char *data, unsigned char *digest) { MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, data, strlen(data)); MD5_Final(digest, &ctx); } void dump(unsigned char *data, int len) { int i; printf("str: "); for(i=0;i<len;i++){ if(data[i] < 32 || data[i] > 126){ printf("."); }else{ printf("%c", data[i]); } } printf("\n"); printf("hex: "); for(i=0;i<len;i++){ printf("%02x ",data[i]); } printf("\n"); } void aes_demo() { char *data = "1234567890123456"; char encdata[1024]; char decdata[1024]; AES_KEY enckey; AES_KEY deckey; uint8_t md5[16]; md5sum(encpass, md5); AES_set_encrypt_key(md5, 128, &enckey); AES_set_decrypt_key(md5, 128, &deckey); printf("===== AES =====\n"); printf("[data]\n"); dump(data, 16); printf("\n[encrypt]\n"); AES_encrypt(data, encdata, &enckey); dump(encdata, 16); printf("\n[decrypt]\n"); AES_decrypt(encdata, decdata, &deckey); dump(decdata, 16); } void blowfish_demo() { BF_LONG data = 0x34333231; BF_KEY key; BF_set_key(&key, 4, "hoge"); printf("===== blowfish =====\n"); printf("[data]\n"); dump((unsigned char *)&data, sizeof(data)); printf("\n[encrypt]\n"); BF_encrypt(&data, &key); dump((unsigned char *)&data, sizeof(data)); printf("\n[decrypt]\n"); BF_decrypt(&data, &key); dump((unsigned char *)&data, sizeof(data)); } int main(int argc, char *argv[]) { aes_demo(); printf("\n"); blowfish_demo(); return(0); }
コンパイル
$ gcc -lssl -o test test.c
実行結果
$ ./test ===== AES ===== [data] str: 1234567890123456 hex: 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 [encrypt] str: vo.c......aG.... hex: 76 6f db 63 cd ef d4 93 ac 8f 61 47 a4 bf ac dc [decrypt] str: 1234567890123456 hex: 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 ===== blowfish ===== [data] str: 1234 hex: 31 32 33 34 [encrypt] str: X.GA hex: 58 c7 47 41 [decrypt] str: 1234 hex: 31 32 33 34
Linuxでcからnetstatっぽいことをしたかった
詳しい話はまた後日ということで、とりあえず今回はソースと実行結果だけ貼り付けます。
netlinkの資料は比較的少ないので、カーネルソースを追いながら試行錯誤してみました。
TCPDIAG_GETSOCKでNLM_F_DUMPを指定するとソケットを列挙できるようです。
sportやdportを指定すると条件にマッチしたソケットだけ列挙してくれるので結構便利そう。
wbuf.req.id.idiag_sport=htons(3306);
とかするとMySQLの接続数を調べることができたりなんかして。
ntest.c
#include<stdio.h> #include<sys/socket.h> #include<linux/netlink.h> #include<linux/inet_diag.h> #include<net/tcp_states.h> int scount(int n) { int r=0; int len; static int seq=1; static char rbuf[65535]; struct { struct nlmsghdr nlh; struct inet_diag_req req; } wbuf; struct nlmsghdr *nlh; struct inet_diag_msg *msg; wbuf.nlh.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(wbuf.req))); wbuf.nlh.nlmsg_type = TCPDIAG_GETSOCK; wbuf.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; wbuf.nlh.nlmsg_seq = seq++; wbuf.req.idiag_family = AF_INET; wbuf.req.idiag_src_len = 0; wbuf.req.idiag_dst_len = 0; wbuf.req.idiag_ext = 0; wbuf.req.idiag_states = TCPF_ESTABLISHED; wbuf.req.idiag_dbs = 0; wbuf.req.id.idiag_sport = 0; wbuf.req.id.idiag_dport = 0; wbuf.req.id.idiag_cookie[0] = INET_DIAG_NOCOOKIE; wbuf.req.id.idiag_cookie[1] = INET_DIAG_NOCOOKIE; len = write(n, &wbuf, wbuf.nlh.nlmsg_len); if(len < 0){ printf("netlink write error!!\n"); return(-1); } while(1){ len=read(n, rbuf, sizeof(rbuf)); for(nlh=(struct nlmsghdr *)rbuf;NLMSG_OK(nlh, len);nlh=NLMSG_NEXT(nlh, len)){ if(nlh->nlmsg_seq != wbuf.nlh.nlmsg_seq) continue; msg=(struct inet_diag_msg *)((char *)nlh + sizeof(struct nlmsghdr)); if(nlh->nlmsg_type == NLMSG_ERROR){ fprintf(stderr,"netlink msg error\n"); return(-1); } if(nlh->nlmsg_type == NLMSG_DONE){ return(r); } /* printf("%02d: state=%02d sport=%05d dport=%05d\n", r, msg->idiag_state, ntohs(msg->id.idiag_sport), ntohs(msg->id.idiag_dport)); */ r++; } } return(0); } int main(int argc, char *argv[]) { int c; int n; n = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG); if(n == -1){ printf("Can't Open NetLink!!\n"); return(0); } c = scount(n); if(c == -1) return(1); printf("%d\n",c); return(0); }
実行結果
$ ./ntest 7 $ netstat --inet -n | grep ESTABLISHED | wc -l 7
カーネルモジュールことはじめ #9
だいぶ間があいてしまいましたが、気を取り直して再開します。「カーネルモジュールことはじめ」といいつつIPVSに偏ったネタばかりですが気にせずに進めます。今日は、こないだ書いたコード を見ながらIPVSの挙動を少しだけ追ってみたいと思います。
IPVSに新しいスケジューラを登録するには、module_init() で指定した初期化関数の中で、register_ip_vs_scheduler() を使います。登録用のエントリとして利用する ip_vs_scheduler 構造体は、include/net/ip_vs.h で以下のように定義されています。
/* * The scheduler object */ struct ip_vs_scheduler { struct list_head n_list; /* d-linked list head */ char *name; /* scheduler name */ atomic_t refcnt; /* reference counter */ struct module *module; /* THIS_MODULE/NULL */ /* scheduler initializing service */ int (*init_service)(struct ip_vs_service *svc); /* scheduling service finish */ int (*done_service)(struct ip_vs_service *svc); /* scheduler updating service */ int (*update_service)(struct ip_vs_service *svc); /* selecting a server from the given service */ struct ip_vs_dest* (*schedule)(struct ip_vs_service *svc, const struct sk_buff *skb); };
この構造体では、以下のメンバに関数ポインタを指定します。
- init_service
- done_service
- update_service
- schedule
今回は、これらの関数がどのタイミングで実行されるのかを調べてみます。こないだ書いたコードでは、それぞれの関数の中から IP_VS_DBG() を呼んでいます。これは、/proc/sys/net/ipv4/vs/debug_level で指定されたデバッグレベル未満のメッセージをカーネルログに出力するものです。ここでは、IP_VS_DBG(7,"") としているので、デバッグレベルを 8に設定します。
# sysctl -w net.ipv4.vs.debug_level=8
そして、ログを眺めながら以下の操作をやってみます
- モジュールをロード
- 仮想サービスを追加
- リアルサーバを追加
- 仮想サービスへ接続
- リアルサーバを削除
- 仮想サービスを削除
# modprobe ip_vs_test # ipvsadm -A -t 10.0.0.1:80 -s test kernel: IPVS: ip_vs_sched_getbyname(): sched_name "test" kernel: IPVS: ip_vs_test: init_svc # ipvsadm -a -t 10.0.0.1:80 -r 192.168.0.1:80 kernel: Enter: ip_vs_add_dest, net/ipv4/ipvs/ip_vs_ctl.c line 780 kernel: Enter: ip_vs_new_dest, net/ipv4/ipvs/ip_vs_ctl.c line 732 kernel: Leave: ip_vs_new_dest, net/ipv4/ipvs/ip_vs_ctl.c line 764 kernel: IPVS: ip_vs_test: update_svc kernel: Leave: ip_vs_add_dest, net/ipv4/ipvs/ip_vs_ctl.c line 869 # telnet 10.0.0.1 80 kernel: IPVS: Bind-dest TCP c:10.211.55.5:2892 v:10.0.0.1:80 d:192.168.0.1:80 fwd:R s:0 conn->flags:183 conn->refcnt:1 dest->refcnt:2 kernel: IPVS: Schedule fwd:R c:10.211.55.5:2892 v:10.0.0.1:80 d:192.168.0.1:80 conn->flags:1C3 conn->refcnt:2 kernel: IPVS: TCP input [S...] 192.168.0.1:80->10.211.55.5:2892 state: NONE->SYN_RECV conn->refcnt:2 kernel: IPVS: Unbind-dest TCP c:10.211.55.5:2892 v:10.0.0.1:80 d:192.168.0.1:80 fwd:R s:3 conn->flags:183 conn->refcnt:1 dest->refcnt:2 # ipvsadm -d -t 10.0.0.1:80 -r 192.168.0.1:80 kernel: Enter: ip_vs_del_dest, net/ipv4/ipvs/ip_vs_ctl.c line 997 kernel: IPVS: ip_vs_test: update_svc kernel: Leave: ip_vs_del_dest, net/ipv4/ipvs/ip_vs_ctl.c line 1024 # ipvsadm -D -t 10.0.0.1:80 kernel: IPVS: ip_vs_test: done_svc
今日のまとめ
init_service
仮想サービスを追加するときに呼び出されます(ipvsadm -A)
done_service
仮想サービスを削除するときに呼び出されます(ipvsadm -D)
update_service
リアルサーバを追加するときに呼び出されます(ipvsadm -a)
リアルサーバを削除するときに呼び出されます(ipvsadm -d)
schedule
仮想サービスへ接続されたときに呼び出されます
SpamAssassin結構いいかも
昨日から Mail-SpamAssassin-3.2.1.tar.gz を入れて様子をみてますが、なかなかいい感じです。でもまあしばらくは大事なメールがSPAM判定されていないかをチェックする日々が続きそうです。
ただ困ったことが一点あります。スパム判定されたメールにはレポートメッセージがマルチパートで追加されて「Content preview:」に元メールの先頭部分が挿入されます。これを見ることで本当にSPAMなのかどうかの確認がしやすくなるのだと思いますが、このレポートメッセージの「Content-Type:」 には'charset=iso-8859-1'が入ってるんですね。そのため、日本語のメールが化けてしまうので、毎回わざわざオリジナルメッセージを確認しなければなりません。Content-Typeは引き継いでくれてもいいような気はするんですけどね。なにか問題あるのかな。
spamassassin-3.2.1 入れてみた
なにげに手作業での処理がめんどくさくなってきたので、いまさらながら Mail-SpamAssassin-3.2.1.tar.gz を入れてみました。とはいえ、今のところなんにも考えずに動かしてみて、なんにも考えなくてもそれなりに動いているので、なんにも書くことはないです(^^;;
このまましばらく様子をみつつ、何かネタが見つかったら出してみたいとおもいます。
#でもあれだ、スパムフィルタって、何も考えずに動かしても期待する動作が得られるのが理想かね(w