0 Replies - 648 Views - Last Post: 09 March 2013 - 01:42 AM Rate Topic: -----

#1 Takk  Icon User is offline

  • D.I.C Head

Reputation: 40
  • View blog
  • Posts: 111
  • Joined: 08-March 13

IRC Bot - vb.net years later, packet splitting, threading, and identd

Posted 09 March 2013 - 01:42 AM

So I know that IRC is of a dying breed, but with undying love. I currently have a server running on a home computer and I find myself using it for all sorts of things. For example, if I want to communicate with my garden monitors when I'm out of town, I can get that hardware to connect to my irc server and issue it commands like we've all done to a million irc bots in the past, and given there is an irc client available for any OS known to man, this works out much nicer than designing a communication protocol for client and server side into everything I build.

Lately I've needed to put together a one-to-many communication system for a project I'm working on and figured that instead of writing a server and client both, I would use the hybrid-ircd package on my ubuntu server, hide some of the unnecessary business and use that as the server..

On top of this, as I was searching on the internet I was having a hard time finding active threads or well functioning code that I could build from, so.. from the pieces I was able to find, some more searching, and a little bit of trial and a lota bit of error, I was able to put together some code which I think will help some people out in the future.

Let me say real quick that I'm not .net expert, by far my challenge here was converting what I knew from vb6 and how things were done then, into vb.net learning as I went. Finally, a big thanks to modi123_1 from this forum for his original post which got me started, and the Ident response code which was borrowed from his post.

While I was working on this, I ran into a few problems and ended up adding threading, packet splitting, and a bit better command handling than I was able to find in any of the other posts on the internet. I'm 100% up for any suggestions that anyone has for improvements and would actually enjoy hearing them, also any questions as far as implementation goes, just drop a response on the thread, the routine is complete, but some of the surrounding code was pulled so you will likely have some errors on c&p. Also, forgive the commenting as this is likely the most comments I've ever included :) Hope you enjoy and make something good of this.


Private Structure sPacket 'We will store our incoming data packets in these
        Dim Prefix As String
        Dim Command As String
        Dim Target As String
        Dim Params As String
        Dim Raw As String
    End Structure

    Private _Server As String = String.Empty 'server address
    Private _Port As UShort = 0 'server port number
    Private _Nickname As String = String.Empty 'your nickname (will be visible in channels)
    Private _Realname As String = String.Empty 'your realname
    Private _Register As String = String.Empty 'your USER registration string "nickname hostname servername realname"

    Dim ConnThread As Thread 'thread for your connection
    Dim IdentThread As Thread 'thread for the identd daemon
    Dim Sock As Socket 'socket for your connection


    Public Event onRawData(Data As String)
    Public Delegate Sub rawDataDel(Message As String)



    Private Sub rawData(Message As String)
        If Me.InvokeRequired Then
            Dim MyDel As New rawDataDel(AddressOf rawData)
            Me.Invoke(MyDel, Message)
        Else
            RaiseEvent onRawData(Message)
        End If

    End Sub


    Public Sub Connect(ByVal Server As String, ByVal Port As UShort, ByVal Nickname As String, ByVal Realname As String, ByVal Register As String)
        'This checks to see if your new values are able to overwrite any default values and still work
        If Server.Length > 1 Then _Server = Server Else onerrorFound("INVALID:1:Server Address") : Exit Sub
        If Port >= 0 Then _Port = Port Else onerrorFound("INVALID:2:Port Number") : Exit Sub
        If Nickname.Length > 1 Then _Nickname = Nickname Else onerrorFound("INVALID:3:Nickname") : Exit Sub
        If Realname.Length > 1 Then _Realname = Realname Else onerrorFound("INVALID:4:Realname") : Exit Sub
        If Register.Length > 1 Then _Register = Register Else _Register = _Nickname & " none none " & _Realname


        IdentThread = New Thread(AddressOf IdentThread_)
        IdentThread.Start()

        ConnThread = New Thread(AddressOf ConnectThread_)
        ConnThread.Start()
    End Sub



    Public Sub SendData(Data As String)
        If Data.IndexOf(vbNewLine) = -1 Then Data &= vbNewLine
        Dim ToSend As Byte() = Encoding.UTF8.GetBytes(Data)
        Sock.Send(ToSend)
    End Sub

    Public Sub ChangeNickname(Nickname As String)
        If Nickname.Length < 2 Then onerrorFound("INVALID:3:Nickname") : Exit Sub
        SendData("NICK " & Nickname)
    End Sub

    Private Sub ChangeRegister(Register As String)
        If Register.Length < 2 Then onerrorFound("INVALID:5:Register") : Exit Sub
        SendData("USER " & Register)
    End Sub

    Private Sub IdentThread_()
        'The only real portion of code that worked well as it was, then dumped into its own thread 
        ' Borrowed from 'modi123_1's' post on this board from quite some time ago
        'http://www.dreamincode.net/forums/topic/238399-irc-bot-101-basic-bot-layout-and-creation/

        Dim identListener As TcpListener = Nothing
        Dim identClient As TcpClient = Nothing
        Dim identNetworkStream As NetworkStream = Nothing
        Dim identStreamReader As StreamReader = Nothing
        Dim identStreamWriter As StreamWriter = Nothing
        Dim identResponseString As String = String.Empty

        identListener = New TcpListener(IPAddress.Any, 113)
        identListener.Start()
        identClient = identListener.AcceptTcpClient
        identListener.Stop()
        identNetworkStream = identClient.GetStream
        identStreamReader = New StreamReader(identNetworkStream)

        identResponseString = identStreamReader.ReadLine
        identStreamWriter = New StreamWriter(identNetworkStream)
        identStreamWriter.WriteLine(String.Format("{0} : USERID : WINDOWS 7 : {1}", identResponseString, _Nickname))
        identStreamWriter.Flush()
    End Sub


    Private Sub ConnectThread_()

        'Our socket setup
        Dim ipAddr As IPAddress = IPAddress.Parse(_Server)
        Dim ipEP As IPEndPoint = New System.Net.IPEndPoint(ipAddr, 12947)

        Sock = New Socket(ipEP.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
        Sock.Connect(_Server, _Port)

        Dim SentReg As Boolean = False 'Have we sent our registration information yet? Not yet.
        Do While Sock.Connected = True

            'Once we connect, the IRC server will be expecting to get our registration information
            'This include the server password, your nickname, and your register string (PASS,NICK,USER)
            If Not SentReg Then
                ChangeNickname(_Nickname)
                ChangeRegister(_Register)
                SentReg = True 'Weve sent our registration info now, dont send it every time the loop fires
            End If

            Dim Data(4096) As Byte
            Sock.Receive(Data, 4096, SocketFlags.None) 'Get any data in the buffer

            'Byte to string conversion of incoming data
            Dim ParseData As String = ASCIIEncoding.UTF8.GetString(Data).Trim 'Bytes are transferred, strings are parsed.


            'This section will break the incoming data at the CrLf's, and add each command string to a list
            'server's can send data very quickly, and you shouldn't in my opinion expect that you can interpret it as quickly
            Dim DataQueue As List(Of String) = New List(Of String) 'create a new list for the data packets
            Dim tempQueue() As String = Split(ParseData, vbNewLine) 'break the data into individual command strings
            For Each e As String In tempQueue
                If Asc(e) <> 0 Then DataQueue.Add(e.Trim) 'drop any of those freaky termination packets i was getting and add to the data list
            Next


            For Each DataItem As String In DataQueue
                'Pass the raw data to the rawData Event, you'll want a way to look at this stuff, will help greatly with parsing it
                rawData(DataItem)

                'snippet from RFC1459 explaining the layout of incoming commands
                'this may be a little confusing if you're new to this protocol, but 
                'once you see some raw data, it's actually pretty simple
                '<message>  ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
                '<prefix>   ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
                '<command>  ::= <letter> { <letter> } | <number> <number> <number>
                '<SPACE>    ::= ' ' { ' ' }
                '<params>   ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
                '<middle>   ::= <Any *non-empty* sequence of octets not including SPACE or NUL or CR or LF, the first of which may not be ':'>
                '<trailing> ::= <Any, possibly *empty*, sequence of octets not including NUL or CR or LF>
                '<crlf>     ::= CR LF

                'sPacket = Prefix - Command - Target - Params
                ':My.Chat NOTICE AUTH :*** Looking up your hostname...


                Dim cPacket As sPacket = New sPacket 'This will hold the parsed data we're currently working with
                Dim tmpP As String = String.Empty 'Temporary holding space for data which still needs to be parsed
                Dim startParam As Integer 'Where do the params start?

                cPacket.Raw = DataItem 'some items will not have a parameter seperater, in which case we will need this (ex: Numeric 004)

                If DataItem.Substring(0, 1) = ":" Then tmpP = DataItem.Remove(0, 1) 'Get rid of the leading colon and initiate our temporary data

                'Capture our command params then remove them from our temporary data
                startParam = tmpP.IndexOf(":") 'Find the beginning of the params
                If startParam > -1 Then
                    cPacket.Params = tmpP.Substring(startParam + 1) 'extract params from data
                    tmpP = tmpP.Remove(startParam).Trim 'remove the params and any residual padding from our temporary data
                End If
                'Our remaining data should look like:
                '   tmpP = "My.Chat NOTICE AUTH"

                Dim tmP2() As String = tmpP.Split(" ") 'quick split to pass the sections into our data packet
                'Try
                With cPacket
                    .Prefix = tmP2(0) 'My.Chat (Origin)
                    If tmP2.Length > 1 Then .Command = tmP2(1) 'NOTICE (Command) if included
                    If tmP2.Length > 2 Then .Target = tmP2(2) 'AUTH (Target) if included
                End With


                'PING messages come in oddly, I like to get rid of these seperate from the rest of the data and get on with my life
                If (cPacket.Prefix.ToUpper = "") Then
                    If cPacket.Raw.Substring(0, 4).ToUpper = "PING" Then
                        SendData("PONG :" & cPacket.Raw.Substring(cPacket.Raw.IndexOf(":") + 1))
                    End If
                Else
                    Select Case cPacket.Command.ToUpper
                        Case "NOTICE"
                            Select Case cPacket.Target
                                Case "AUTH" 'authorization packets
                                Case _Nickname 'server message
                                Case Else 'something else
                            End Select
                        Case "MODE"
                            Select Case cPacket.Target
                                Case _Nickname 'client mode change
                                Case Else 'other mode change
                            End Select

                        Case Else
                            'IRC numeric message
                            Select Case cPacket.Command
                                Case clsNumerics.RPL_WELCOME 'const = 001 'Received Welcome message from server
                                Case clsNumerics.RPL_MOTD 'const = 372 'Received MOTD line from sever
                            End Select

                    End Select
                End If
            Next
        Loop

    End Sub



Usage:
Connect("Server.IP.address, PortNumber, "Nickname", "Realname", "")



Original Post: http://www.dreaminco...t-and-creation/

Is This A Good Question/Topic? 0
  • +

Page 1 of 1