みなさんは、IRC(Internet Relay Chat)というワードを聞いたことはあるでしょうか。
今IRCを使っているという人は少ないかもしれません(私の周りにも一人もいません)が、今回はこのIRCについて書いていきます!
(すでにIRCクライアントのコードが載っているQiitaの記事がありましたが、気になる部分がいくつかあったのと、Python2のコードだったので、改めて記事にしようと思いました。)
#IRC(Internet Relay Chat)とは
IRCとは、サーバを介してクライアントとクライアントが会話をする枠組みで、インスタントメッセンジャーのプロトコルの1つに分類されます。
また、ユーザがクライアントソフトを実行し、サーバは情報の伝達を行うだけなので、サーバの負担が軽くチャットが高速に行えます。
チャットに特化したプロトコルなんですね!
##なぜIRCの利用は衰退したの?
チャットアプリなんて、今はたくさんありますよね。(Facebook,Twitter,Discord,Slack,...etc.)
これらSNSを利用するほうが、IRCを利用するよりも便利で多機能で使いやすいわけですね。。。
また、SSL/TLSを利用せずにIRCを利用すると、暗号化していないため、第三者に会話の内容を盗み見られる可能性があります。(もちろんSSL/TLSを利用すれば安全です!)
LINEなどのサービスなら、暗号化などは自動でやってくれるから、そんなの普段は意識しないですよね。。
##IRCを利用するメリットは?
それだけ聞くとIRCを利用するメリットはあまりないように感じるかもしれませんが、そんなことはありません!
まず、IRCは、データ通信に関するプロトコルが簡素で、かつオープンであるため、ユーザ・クライアントに用いるソフトウェアの開発が簡単なんです!今回の記事でも、簡易クライアントをたった100行程度で作っちゃいます!
それだけではなく、IRCサーバを立てるのも、ngircdというものを利用すれば超簡単なんです!(今回はこれには触れません。)
##今IRCは何に使われてるの?
では今、IRCは何に使われているんでしょうか。
それは、、、ボットネットの通信手段です。(誤解を招きそうなので言っておきますが、もちろん正規の普通の使い方でもたくさん利用されています。)
ボットネットとは、ボットウイルスに感染した端末で構成されるネットワークです。ボットウイルスとは、マルウェアの一種で、感染すると端末が悪意を持った第三者に遠隔操作され、不正アクセスやDDoS攻撃などの犯罪行為に使われてしまいます。
その感染端末と攻撃者の通信手段に、IRCはよく使われています。。。
自分のパソコンをポートスキャンしたり、通信のログを見て、「IRC」という文字を見つけた場合は、ボットウイルスに感染している可能性があるかもしれません。。。
##有名なIRCクライアントソフト
IRCクライアントソフトはたくさんありますが、ここでは中でも有名(人気)なもの(すべて無料)を紹介します。
###LimeChat
おそらく1番有名(人気)なIRCクライアントソフトです。
Windowsの場合はLimeChat 2.x、Macの場合はLimeChat for OSXを利用します。
現行IRCクライアントの中では最も安定しています。ヘルプが充実しているので初心者にもおすすめです。
###HexChat
XChat(後述)ベースのソフトウェアです。
Windows用、Mac用、Unix系システム用があり、ほぼすべてのOSで利用できます。
海外のさまざまなネットワークが標準設定で入っています。
###XChat
Linuxのほとんどのディストリビューションに標準で入っているIRCクライアントです。
Windows用、Mac用、Unix系システム用があり、ほぼすべてのOSで利用できます。
海外のさまざまなネットワークが標準設定で入っています。
#Python3で簡易クライアントを作る
では早速IRCクライアントを作ってみましょう!
IRCクライアントを作るには、まずIRCのコマンドというものについて知る必要があります。
##IRCのコマンドについて
IRCにおいて、サーバとクライアントは互いにメッセージを送信し合います。
また、メッセージが正しいコマンドを含んでいた場合、クライアントは仕様通りのリプライを期待します。
つまり、あるコマンドを含むメッセージに対するサーバからの応答メッセージの形式は決まっているわけですね。
このIRCコマンドはたくさんありますが、今回は、後で作成する簡易クライアントが実装している、最も基本的なコマンド8つについてのみ説明します。(コマンドは正規表現で示します。)
###PASSコマンド
PASS <password>
IRCサーバにパスワードが設定されている場合は、ユーザは接続を開始しようとする前(NICK/USERの組み合わせを送る前)にPASSコマンドを送る必要があります。
###NICKコマンド
NICK :<nickname>
ユーザにニックネームを設定したり、今のニックネームを変更したりするのに使います。
###USERコマンド
USER <username> <hostname> <servername> <realname>
新しいユーザのユーザ名やホスト名、本名を指定するために、接続のはじめに使われます。
###QUITコマンド
QUIT (:<Quit Message>)??
クライアントセッションを終了します。サーバはクライアントにERRORメッセージを送ることでこれを承認します。
###JOINコマンド
JOIN :<channel> ("," <channel>)* (<key> ("," <key>)*)??
ユーザによって、特定のチャンネルに接続するリクエストを行うために使用されます。
###PRIVMSGコマンド
PRIVMASG <msgtarget> :<text to be sent>
ユーザ間のプライベートメッセージを送るのに使います。また、メッセージをチャンネルに送るのにも使われます。msgtargetは通常、メッセージの受け取り手のニックネームか、チャンネル名です。
誰かがメッセージを送った場合は、サーバから次の形式でメッセージが送られてきます。
:<A's nickname>!(~<A's username>)??@<A's gateway-name> PRIVMSG <msgtarget> :<text to be sent>
###PINGコマンド
PING :<server1> (<server2>)??
ネットワーク上のアクティブなクライアントやサーバが、実際につながって動いているかどうかを確認するのに使われます。
サーバは、ネットワークからほかのアクションが届かない場合、一定間隔でPINGメッセージを送出します。
指定時間内にPINGメッセージに返答がない場合、その接続は閉じられます。
PINGメッセージを受け取ったら、server1(PINGメッセージを送ったサーバ)へのリプライとして、可能な限り早く正しいPONGメッセージを送らなくてはなりません。server2パラメータが指定されていれば、それがpingのターゲットとなり、メッセージはそこに転送されます。
###PONGコマンド
PONG <server1> (<server2>)??
PINGメッセージへのリプライです。server2パラメータが指定されれば、メッセージはそこに転送されなくてはなりません。
##IRCクライアント(Python3コード)
import sys, socket, os, signal
HOST = "X.X.X.X"
PORT = 6667 #IRCサーバでは一般的に6667番ポートが使われることが多い
BUF_SIZE = 1024
class IRC(object):
def __init__(self):
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #socketオブジェクトの生成(TCP)
def connect(self, host, port):
self.server.connect((host, port)) #接続
def login(self, password, nickname, username, realname, hostname = "hostname", servername = "*"):
if password is not None: #中にはパスワードがいらないサーバもある
pass_message = "PASS " + password + "\n" #PASSメッセージ
self.server.send(pass_message.encode('utf-8')) #送信
nick_message = "NICK " + nickname + "\n" #NICKメッセージ
user_message = "USER %s %s %s :%s\n" % (username, hostname, servername, realname) #USERメッセージ
self.server.send(nick_message.encode('utf-8')) #送信
self.server.send(user_message.encode('utf-8')) #送信
def join(self, channel):
join_message = "JOIN " + channel + "\n" #JOINメッセージ
self.server.send(join_message.encode('utf-8')) #送信
def pong(self, server1, server2 = None):
pong_message = "PONG %s %s" % (server1, server2) #PONGメッセージ
pong_message += "\n"
self.server.send(pong_message.encode('utf-8')) #送信
def privmsg(self, channel, text):
privmsg_message = "PRIVMSG %s :%s\n" % (channel, text) #PRIVMSGメッセージ
self.server.send(privmsg_message.encode('utf-8')) #送信
def quit(self):
self.server.send(b"QUIT :bye!") #QUITメッセージ送信
def handle_privmsg(self, prefix, text):
print("\r" + prefix + ">" + text + "\n>", end="") #受信したPRIVMSGメッセージを処理、表示
def wait_message(self):
while(True):
msg_buf = self.server.recv(BUF_SIZE) #受信
msg_buf = msg_buf.decode('utf-8').strip()
## ここからメッセージ処理 ##
prefix = None
if msg_buf[0] == ":":
p = msg_buf.find(" ")
prefix = msg_buf[1:p]
msg_buf = msg_buf[(p + 1):]
p = msg_buf.find(":")
if p != -1: #":"から始まるパラメータがまだあった場合
last_param = msg_buf[(p + 1):]
msg_buf = msg_buf[:p]
msg_buf = msg_buf.strip()
messages = msg_buf.split()
## ここまで ##
command = messages[0] #コマンド名
params = messages[1:] #今回は無視
if command == "PING":
self.pong(last_param) #PINGが来たらすぐPONGを返す
elif command == "PRIVMSG":
text = last_param #PRIVMSGコマンドで送られてきたメッセージ
self.handle_privmsg(prefix, text)
def client_interface(self, channel, prompt = ">"):
while(True):
line = input(prompt)
if line == "quit":
self.quit()
break
self.privmsg(channel, line)
def main():
password = "password"
nickname = "nickhoge"
username = "usr"
realname = "realname"
channel = "#test_channel"
irc = IRC()
irc.connect(HOST, PORT)
irc.login(password, nickname, username, realname)
irc.join(channel)
pid = os.fork() #子プロセス生成
if(pid == 0): #os.fork()は、子プロセスでは0を返す
irc.wait_message()
else:
irc.client_interface(channel)
os.kill(pid, signal.SIGTERM) #子プロセスをkill(これをしないと子プロセスがゾンビプロセスになる)
if __name__ == "__main__":
main()
##macで実行してみた
実際にLimeChatユーザと会話してみました。
LimeChat側からみるとこんな感じです。
いい感じですね!
#参考
https://qiita.com/mmttt202/items/d182c6f27f466c923fea
http://jbpe.tripod.com/rfcj/rfc2812.j.sjis.txt
https://www.friend-chat.jp/irc.html
https://wikiwiki.jp/2chIRC/IRC%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E7%B4%B9%E4%BB%8B
https://ja.wikipedia.org/wiki/Internet_Relay_Chat
https://murashun.jp/blog/20190215-01.html