├── .gitignore ├── LICENSE ├── README.md ├── constants.go ├── examples ├── rx_example.go ├── trx_example.go ├── tx_example.go ├── tx_multipart_example.go └── tx_multipart_ucs2_example.go ├── gsmutil └── ucs2.go ├── pdu.go ├── pdu_bind.go ├── pdu_bind_resp.go ├── pdu_deliver_sm.go ├── pdu_deliver_sm_resp.go ├── pdu_enquire_link.go ├── pdu_enquire_link_resp.go ├── pdu_fields.go ├── pdu_generic_nack.go ├── pdu_header.go ├── pdu_query_sm.go ├── pdu_query_sm_resp.go ├── pdu_submit_sm.go ├── pdu_submit_sm_resp.go ├── pdu_test.go ├── pdu_tlv_field.go ├── pdu_unbind.go ├── pdu_unbind_resp.go ├── receiver.go ├── smpp.go ├── smpp_test.go ├── transceiver.go └── transmitter.go /.gitignore: -------------------------------------------------------------------------------- 1 | bench_test.go 2 | bench/client_bench.go -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Kevin Patel 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SMPP34 2 | ====== 3 | 4 | Check [examples](https://github.com/CodeMonkeyKevin/smpp34/tree/master/examples) folder for usage details 5 | 6 | ##### Supported/Unsupported PDUs 7 | - [x] bind_transmitter 8 | - [x] bind_transmitter_resp 9 | - [x] bind_receiver 10 | - [x] bind_receiver_resp 11 | - [x] bind_transceiver 12 | - [x] bind_transceiver_resp 13 | - [ ] outbind 14 | - [x] unbind 15 | - [x] unbind_resp 16 | - [x] submit_sm 17 | - [x] submit_sm_resp 18 | - [ ] submit_sm_multi 19 | - [ ] submit_sm_multi_resp 20 | - [ ] data_sm 21 | - [ ] data_sm_resp 22 | - [x] deliver_sm 23 | - [x] deliver_sm_resp 24 | - [x] query_sm 25 | - [x] query_sm_resp 26 | - [ ] cancel_sm 27 | - [ ] cancel_sm_resp 28 | - [ ] replace_sm 29 | - [ ] replace_sm_resp 30 | - [x] enquire_link 31 | - [x] enquire_link_resp 32 | - [ ] alert_notification 33 | - [x] generic_nack 34 | 35 | ## License 36 | 37 | This repository is Copyright (c) 2013 Kevin Patel. All rights reserved. 38 | It is licensed under the MIT license. Please see the LICENSE file for applicable license terms. 39 | 40 | ## Author 41 | 42 | [Kevin Patel](https://github.com/CodeMonkeyKevin) -------------------------------------------------------------------------------- /constants.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | const ( 4 | // SMPP Protocol Version 5 | SMPP_VERSION = 0x34 6 | 7 | // Max PDU size to minimize some attack vectors 8 | MAX_PDU_SIZE = 4096 // 4KB 9 | 10 | // Sequence number start/end 11 | SEQUENCE_NUM_START = 0x00000001 12 | SEQUENCE_NUM_END = 0x7FFFFFFF 13 | ) 14 | 15 | const ( 16 | // ESME Error Constants 17 | ESME_ROK CMDStatus = 0x00000000 // OK! 18 | ESME_RINVMSGLEN CMDStatus = 0x00000001 // Message Length is invalid 19 | ESME_RINVCMDLEN CMDStatus = 0x00000002 // Command Length is invalid 20 | ESME_RINVCMDID CMDStatus = 0x00000003 // Invalid Command ID 21 | ESME_RINVBNDSTS CMDStatus = 0x00000004 // Incorrect BIND Status for given command 22 | ESME_RALYBND CMDStatus = 0x00000005 // ESME Already in Bound State 23 | ESME_RINVPRTFLG CMDStatus = 0x00000006 // Invalid Priority Flag 24 | ESME_RINVREGDLVFLG CMDStatus = 0x00000007 // Invalid Registered Delivery Flag 25 | ESME_RSYSERR CMDStatus = 0x00000008 // System Error 26 | ESME_RINVSRCADR CMDStatus = 0x0000000A // Invalid Source Address 27 | ESME_RINVDSTADR CMDStatus = 0x0000000B // Invalid Dest Addr 28 | ESME_RINVMSGID CMDStatus = 0x0000000C // Message ID is invalid 29 | ESME_RBINDFAIL CMDStatus = 0x0000000D // Bind Failed 30 | ESME_RINVPASWD CMDStatus = 0x0000000E // Invalid Password 31 | ESME_RINVSYSID CMDStatus = 0x0000000F // Invalid System ID 32 | ESME_RCANCELFAIL CMDStatus = 0x00000011 // Cancel SM Failed 33 | ESME_RREPLACEFAIL CMDStatus = 0x00000013 // Replace SM Failed 34 | ESME_RMSGQFUL CMDStatus = 0x00000014 // Message Queue Full 35 | ESME_RINVSERTYP CMDStatus = 0x00000015 // Invalid Service Type 36 | ESME_RINVNUMDESTS CMDStatus = 0x00000033 // Invalid number of destinations 37 | ESME_RINVDLNAME CMDStatus = 0x00000034 // Invalid Distribution List name 38 | ESME_RINVDESTFLAG CMDStatus = 0x00000040 // Destination flag is invalid 39 | ESME_RINVSUBREP CMDStatus = 0x00000042 // Invalid 'submit with replace' request 40 | ESME_RINVESMCLASS CMDStatus = 0x00000043 // Invalid esm_class field data 41 | ESME_RCNTSUBDL CMDStatus = 0x00000044 // Cannot Submit to Distribution List 42 | ESME_RSUBMITFAIL CMDStatus = 0x00000045 // submit_sm or submit_multi failed 43 | ESME_RINVSRCTON CMDStatus = 0x00000048 // Invalid Source address TON 44 | ESME_RINVSRCNPI CMDStatus = 0x00000049 // Invalid Source address NPI 45 | ESME_RINVDSTTON CMDStatus = 0x00000050 // Invalid Destination address TON 46 | ESME_RINVDSTNPI CMDStatus = 0x00000051 // Invalid Destination address NPI 47 | ESME_RINVSYSTYP CMDStatus = 0x00000053 // Invalid system_type field 48 | ESME_RINVREPFLAG CMDStatus = 0x00000054 // Invalid replace_if_present flag 49 | ESME_RINVNUMMSGS CMDStatus = 0x00000055 // Invalid number of messages 50 | ESME_RTHROTTLED CMDStatus = 0x00000058 // Throttling error 51 | ESME_RINVSCHED CMDStatus = 0x00000061 // Invalid Scheduled Delivery Time 52 | ESME_RINVEXPIRY CMDStatus = 0x00000062 // Invalid message validity period (Expiry time) 53 | ESME_RINVDFTMSGID CMDStatus = 0x00000063 // Predefined Message Invalid or Not Found 54 | ESME_RX_T_APPN CMDStatus = 0x00000064 // ESME Receiver Temporary App Error Code 55 | ESME_RX_P_APPN CMDStatus = 0x00000065 // ESME Receiver Permanent App Error Code 56 | ESME_RX_R_APPN CMDStatus = 0x00000066 // ESME Receiver Reject Message Error Code 57 | ESME_RQUERYFAIL CMDStatus = 0x00000067 // Query_sm request failed 58 | ESME_RINVOPTPARSTREAM CMDStatus = 0x000000C0 // Error in the optional part of the PDU Body 59 | ESME_ROPTPARNOTALLWD CMDStatus = 0x000000C1 // Optional Parameter not allowed 60 | ESME_RINVPARLEN CMDStatus = 0x000000C2 // Invalid Parameter Length 61 | ESME_RMISSINGOPTPARAM CMDStatus = 0x000000C3 // Expected Optional Parameter missing 62 | ESME_RINVOPTPARAMVAL CMDStatus = 0x000000C4 // Invalid Optional Parameter Value 63 | ESME_RDELIVERYFAILURE CMDStatus = 0x000000FE // Delivery Failure (used for data_sm_resp) 64 | ESME_RUNKNOWNERR CMDStatus = 0x000000FF // Unknown Error 65 | ) 66 | 67 | const ( 68 | // PDU Types 69 | GENERIC_NACK CMDId = 0x80000000 70 | BIND_RECEIVER CMDId = 0x00000001 71 | BIND_RECEIVER_RESP CMDId = 0x80000001 72 | BIND_TRANSMITTER CMDId = 0x00000002 73 | BIND_TRANSMITTER_RESP CMDId = 0x80000002 74 | QUERY_SM CMDId = 0x00000003 75 | QUERY_SM_RESP CMDId = 0x80000003 76 | SUBMIT_SM CMDId = 0x00000004 77 | SUBMIT_SM_RESP CMDId = 0x80000004 78 | DELIVER_SM CMDId = 0x00000005 79 | DELIVER_SM_RESP CMDId = 0x80000005 80 | UNBIND CMDId = 0x00000006 81 | UNBIND_RESP CMDId = 0x80000006 82 | REPLACE_SM CMDId = 0x00000007 83 | REPLACE_SM_RESP CMDId = 0x80000007 84 | CANCEL_SM CMDId = 0x00000008 85 | CANCEL_SM_RESP CMDId = 0x80000008 86 | BIND_TRANSCEIVER CMDId = 0x00000009 87 | BIND_TRANSCEIVER_RESP CMDId = 0x80000009 88 | OUTBIND CMDId = 0x0000000B 89 | ENQUIRE_LINK CMDId = 0x00000015 90 | ENQUIRE_LINK_RESP CMDId = 0x80000015 91 | SUBMIT_MULTI CMDId = 0x00000021 92 | SUBMIT_MULTI_RESP CMDId = 0x80000021 93 | ALERT_NOTIFICATION CMDId = 0x00000102 94 | DATA_SM CMDId = 0x00000103 95 | DATA_SM_RESP CMDId = 0x80000103 96 | ) 97 | 98 | const ( 99 | // FIELDS 100 | SYSTEM_ID = "system_id" 101 | PASSWORD = "password" 102 | SYSTEM_TYPE = "system_type" 103 | INTERFACE_VERSION = "interface_version" 104 | ADDR_TON = "addr_ton" 105 | ADDR_NPI = "addr_npi" 106 | ADDRESS_RANGE = "address_range" 107 | SERVICE_TYPE = "service_type" 108 | SOURCE_ADDR_TON = "source_addr_ton" 109 | SOURCE_ADDR_NPI = "source_addr_npi" 110 | SOURCE_ADDR = "source_addr" 111 | DEST_ADDR_TON = "dest_addr_ton" 112 | DEST_ADDR_NPI = "dest_addr_npi" 113 | DESTINATION_ADDR = "destination_addr" 114 | ESM_CLASS = "esm_class" 115 | PROTOCOL_ID = "protocol_id" 116 | PRIORITY_FLAG = "priority_flag" 117 | SCHEDULE_DELIVERY_TIME = "schedule_delivery_time" 118 | VALIDITY_PERIOD = "validity_period" 119 | REGISTERED_DELIVERY = "registered_delivery" 120 | REPLACE_IF_PRESENT_FLAG = "replace_if_present_flag" 121 | DATA_CODING = "data_coding" 122 | SM_DEFAULT_MSG_ID = "sm_default_msg_id" 123 | SM_LENGTH = "sm_length" 124 | SHORT_MESSAGE = "short_message" 125 | MESSAGE_ID = "message_id" 126 | FINAL_DATE = "final_date" 127 | MESSAGE_STATE = "message_state" 128 | ERROR_CODE = "error_code" 129 | ) 130 | 131 | const ( 132 | // Optional Field Tags 133 | DEST_ADDR_SUBUNIT = 0x0005 134 | DEST_NETWORK_TYPE = 0x0006 135 | DEST_BEARER_TYPE = 0x0007 136 | DEST_TELEMATICS_ID = 0x0008 137 | SOURCE_ADDR_SUBUNIT = 0x000D 138 | SOURCE_NETWORK_TYPE = 0x000E 139 | SOURCE_BEARER_TYPE = 0x000F 140 | SOURCE_TELEMATICS_ID = 0x0010 141 | QOS_TIME_TO_LIVE = 0x0017 142 | PAYLOAD_TYPE = 0x0019 143 | ADDITIONAL_STATUS_INFO_TEXT = 0x001D 144 | RECEIPTED_MESSAGE_ID = 0x001E 145 | MS_MSG_WAIT_FACILITIES = 0x0030 146 | PRIVACY_INDICATOR = 0x0201 147 | SOURCE_SUBADDRESS = 0x0202 148 | DEST_SUBADDRESS = 0x0203 149 | USER_MESSAGE_REFERENCE = 0x0204 150 | USER_RESPONSE_CODE = 0x0205 151 | SOURCE_PORT = 0x020A 152 | DESTINATION_PORT = 0x020B 153 | SAR_MSG_REF_NUM = 0x020C 154 | LANGUAGE_INDICATOR = 0x020D 155 | SAR_TOTAL_SEGMENTS = 0x020E 156 | SAR_SEGMENT_SEQNUM = 0x020F 157 | SC_INTERFACE_VERSION = 0x0210 158 | CALLBACK_NUM_PRES_IND = 0x0302 159 | CALLBACK_NUM_ATAG = 0x0303 160 | NUMBER_OF_MESSAGES = 0x0304 161 | CALLBACK_NUM = 0x0381 162 | DPF_RESULT = 0x0420 163 | SET_DPF = 0x0421 164 | MS_AVAILABILITY_STATUS = 0x0422 165 | NETWORK_ERROR_CODE = 0x0423 166 | MESSAGE_PAYLOAD = 0x0424 167 | DELIVERY_FAILURE_REASON = 0x0425 168 | MORE_MESSAGES_TO_SEND = 0x0426 169 | DR_MESSAGE_STATE = 0x0427 170 | USSD_SERVICE_OP = 0x0501 171 | DISPLAY_TIME = 0x1201 172 | SMS_SIGNAL = 0x1203 173 | MS_VALIDITY = 0x1204 174 | ALERT_ON_MESSAGE_DELIVERY = 0x130C 175 | ITS_REPLY_TYPE = 0x1380 176 | ITS_SESSION_INFO = 0x1383 177 | ) 178 | 179 | const ( 180 | // Encoding Types 181 | ENCODING_DEFAULT = 0x00 // SMSC Default 182 | ENCODING_IA5 = 0x01 // IA5 (CCITT T.50)/ASCII (ANSI X3.4) 183 | ENCODING_BINARY = 0x02 // Octet unspecified (8-bit binary) 184 | ENCODING_ISO88591 = 0x03 // Latin 1 (ISO-8859-1) 185 | ENCODING_BINARY2 = 0x04 // Octet unspecified (8-bit binary) 186 | ENCODING_JIS = 0x05 // JIS (X 0208-1990) 187 | ENCODING_ISO88595 = 0x06 // Cyrillic (ISO-8859-5) 188 | ENCODING_ISO88598 = 0x07 // Latin/Hebrew (ISO-8859-8) 189 | ENCODING_ISO10646 = 0x08 // UCS2 (ISO/IEC-10646) 190 | ENCODING_PICTOGRAM = 0x09 // Pictogram Encoding 191 | ENCODING_ISO2022JP = 0x0A // ISO-2022-JP (Music Codes) 192 | ENCODING_EXTJIS = 0x0D // Extended Kanji JIS (X 0212-1990) 193 | ENCODING_KSC5601 = 0x0E // KS C 5601 194 | ) 195 | 196 | const ( 197 | // ESM_CLASS Types 198 | ESM_CLASS_MSGMODE_DEFAULT = 0x00 // Default SMSC mode (e.g. Store and Forward) 199 | ESM_CLASS_MSGMODE_DATAGRAM = 0x01 // Datagram mode 200 | ESM_CLASS_MSGMODE_FORWARD = 0x02 // Forward (i.e. Transaction) mode 201 | ESM_CLASS_MSGMODE_STOREFORWARD = 0x03 // Store and Forward mode (use this to select Store and Forward mode if Default mode is not Store and Forward) 202 | 203 | ESM_CLASS_MSGTYPE_DEFAULT = 0x00 // Default message type (i.e. normal message) 204 | ESM_CLASS_MSGTYPE_DELIVERYACK = 0x08 // Message containts ESME Delivery Acknowledgement 205 | ESM_CLASS_MSGTYPE_USERACK = 0x10 // Message containts ESME Manual/User Acknowledgement 206 | 207 | ESM_CLASS_GSMFEAT_NONE = 0x00 // No specific features selected 208 | ESM_CLASS_GSMFEAT_UDHI = 0x40 // UDHI Indicator (only relevant for MT msgs) 209 | ESM_CLASS_GSMFEAT_REPLYPATH = 0x80 // Set Reply Path (only relevant for GSM net) 210 | ESM_CLASS_GSMFEAT_UDHIREPLYPATH = 0xC0 // Set UDHI and Reply Path (for GSM net) 211 | ) 212 | -------------------------------------------------------------------------------- /examples/rx_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | smpp "github.com/CodeMonkeyKevin/smpp34" 6 | ) 7 | 8 | func main() { 9 | // connect and bind 10 | rx, err := smpp.NewReceiver( 11 | "localhost", 12 | 9000, 13 | 5, 14 | smpp.Params{ 15 | "system_type": "CMT", 16 | "system_id": "hugo", 17 | "password": "ggoohu", 18 | }, 19 | ) 20 | if err != nil { 21 | fmt.Println("Connection Err:", err) 22 | return 23 | } 24 | 25 | for { 26 | pdu, err := rx.Read() // This is blocking 27 | if err != nil { 28 | fmt.Println("Read Err:", err) 29 | break 30 | } 31 | 32 | // EnquireLinks are auto handles 33 | switch pdu.GetHeader().Id { 34 | case smpp.DELIVER_SM: 35 | // received Deliver Sm 36 | for _, v := range pdu.MandatoryFieldsList() { 37 | f := pdu.GetField(v) 38 | fmt.Println(v, ":", f) 39 | } 40 | 41 | // Respond back to Deliver SM with Deliver SM Resp 42 | err := rx.DeliverSmResp(pdu.GetHeader().Sequence, smpp.ESME_ROK) 43 | 44 | if err != nil { 45 | fmt.Println("DeliverSmResp err:", err) 46 | } 47 | default: 48 | // ignore all other PDUs or do what you link with them 49 | fmt.Println("PDU ID:", pdu.GetHeader().Id) 50 | } 51 | } 52 | 53 | fmt.Println("ending...") 54 | } 55 | -------------------------------------------------------------------------------- /examples/trx_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | smpp "github.com/CodeMonkeyKevin/smpp34" 6 | ) 7 | 8 | func main() { 9 | // connect and bind 10 | trx, err := smpp.NewTransceiver( 11 | "localhost", 12 | 9000, 13 | 5, 14 | smpp.Params{ 15 | "system_type": "CMT", 16 | "system_id": "hugo", 17 | "password": "ggoohu", 18 | }, 19 | ) 20 | if err != nil { 21 | fmt.Println("Connection Err:", err) 22 | return 23 | } 24 | 25 | // Send SubmitSm 26 | seq, err := trx.SubmitSm("test", "test2", "msg", &smpp.Params{}) 27 | 28 | // Pdu gen errors 29 | if err != nil { 30 | fmt.Println("SubmitSm err:", err) 31 | } 32 | 33 | // Should save this to match with message_id 34 | fmt.Println("seq:", seq) 35 | 36 | // start reading PDUs 37 | for { 38 | pdu, err := trx.Read() // This is blocking 39 | if err != nil { 40 | break 41 | } 42 | 43 | // Transceiver auto handles EnquireLinks 44 | switch pdu.GetHeader().Id { 45 | case smpp.SUBMIT_SM_RESP: 46 | // message_id should match this with seq message 47 | fmt.Println("MSG ID:", pdu.GetField("message_id").Value()) 48 | case smpp.DELIVER_SM: 49 | // received Deliver Sm 50 | 51 | // Print all fields 52 | for _, v := range pdu.MandatoryFieldsList() { 53 | f := pdu.GetField(v) 54 | fmt.Println(v, ":", f) 55 | } 56 | 57 | // Respond back to Deliver SM with Deliver SM Resp 58 | err := trx.DeliverSmResp(pdu.GetHeader().Sequence, smpp.ESME_ROK) 59 | 60 | if err != nil { 61 | fmt.Println("DeliverSmResp err:", err) 62 | } 63 | default: 64 | fmt.Println("PDU ID:", pdu.GetHeader().Id) 65 | } 66 | } 67 | 68 | fmt.Println("ending...") 69 | } 70 | -------------------------------------------------------------------------------- /examples/tx_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | smpp "github.com/CodeMonkeyKevin/smpp34" 6 | ) 7 | 8 | func main() { 9 | // connect and bind 10 | tx, err := smpp.NewTransmitter( 11 | "localhost", 12 | 9000, 13 | 5, 14 | smpp.Params{ 15 | "system_type": "CMT", 16 | "system_id": "hugo", 17 | "password": "ggoohu", 18 | }, 19 | ) 20 | if err != nil { 21 | fmt.Println("Connection Err:", err) 22 | return 23 | } 24 | 25 | // Send SubmitSm 26 | seq, err := tx.SubmitSm("test", "test2", "msg", &smpp.Params{}) 27 | 28 | // Pdu gen errors 29 | if err != nil { 30 | fmt.Println("SubmitSm err:", err) 31 | } 32 | 33 | // Should save this to match with message_id 34 | fmt.Println("seq:", seq) 35 | 36 | for { 37 | pdu, err := tx.Read() // This is blocking 38 | if err != nil { 39 | fmt.Println("Read Err:", err) 40 | break 41 | } 42 | 43 | // EnquireLinks are auto handles 44 | switch pdu.GetHeader().Id { 45 | case smpp.SUBMIT_SM_RESP: 46 | // message_id should match this with seq message 47 | fmt.Println("MSG ID:", pdu.GetField("message_id").Value()) 48 | default: 49 | // ignore all other PDUs or do what you link with them 50 | fmt.Println("PDU ID:", pdu.GetHeader().Id) 51 | } 52 | } 53 | 54 | fmt.Println("ending...") 55 | } 56 | -------------------------------------------------------------------------------- /examples/tx_multipart_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | smpp "github.com/mergenchik/smpp34" 7 | "math" 8 | ) 9 | 10 | func main() { 11 | // connect and bind 12 | tx, err := smpp.NewTransmitter( 13 | "localhost", 14 | 9000, 15 | 5, 16 | smpp.Params{ 17 | "system_type": "CMT", 18 | "system_id": "hugo", 19 | "password": "ggoohu", 20 | }, 21 | ) 22 | if err != nil { 23 | fmt.Println("Connection Err:", err) 24 | return 25 | } 26 | msg := "Very Long Message, 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890END" 27 | sm_len := len(msg) 28 | fmt.Println("Message Bytes count:", sm_len) 29 | 30 | if sm_len > 140 { 31 | sm_runes := []byte(msg) 32 | rune_len := len(sm_runes) 33 | send_params := smpp.Params{smpp.DATA_CODING: smpp.ENCODING_DEFAULT, smpp.ESM_CLASS: smpp.ESM_CLASS_GSMFEAT_UDHI} 34 | total_parts := byte(int(math.Ceil(float64(rune_len) / 134))) 35 | partNum := 1 36 | uid := make([]byte, 1) 37 | _, err := rand.Read(uid) 38 | if err != nil { 39 | // fmt.Println("QuerySM error:", err) 40 | fmt.Println("Rand.Read error:", err) 41 | return 42 | } 43 | for i := 0; i < rune_len; i += 134 { 44 | start := i 45 | end := i + 134 46 | if end > rune_len { 47 | end = rune_len 48 | } 49 | part := []byte{0x05, 0x00, 0x03, uid[0], total_parts, byte(partNum)} 50 | part = append(part, []byte(sm_runes[start:end])...) 51 | fmt.Println("Part:", part) 52 | // Send SubmitSm 53 | seq, err := tx.SubmitSmEncoded("test", "test2", part, &send_params) 54 | // Pdu gen errors 55 | if err != nil { 56 | fmt.Println("SubmitSm err:", err) 57 | } 58 | // Should save this to match with message_id 59 | fmt.Println("seq:", seq) 60 | partNum++ 61 | 62 | } 63 | 64 | } else { 65 | send_params := smpp.Params{} 66 | // Send SubmitSm 67 | seq, err := tx.SubmitSm("test", "test2", msg, &send_params) 68 | 69 | // Pdu gen errors 70 | if err != nil { 71 | fmt.Println("SubmitSm err:", err) 72 | } 73 | // Should save this to match with message_id 74 | fmt.Println("seq:", seq) 75 | 76 | } 77 | 78 | for { 79 | pdu, err := tx.Read() // This is blocking 80 | if err != nil { 81 | fmt.Println("Read Err:", err) 82 | break 83 | } 84 | 85 | // EnquireLinks are auto handles 86 | switch pdu.GetHeader().Id { 87 | case smpp.SUBMIT_SM_RESP: 88 | // message_id should match this with seq message 89 | fmt.Println("MSG ID:", pdu.GetField("message_id").Value()) 90 | fmt.Printf("PDU Header: %v", pdu.GetHeader()) 91 | fmt.Println() 92 | default: 93 | // ignore all other PDUs or do what you link with them 94 | fmt.Println("PDU ID:", pdu.GetHeader().Id) 95 | } 96 | } 97 | 98 | fmt.Println("ending...") 99 | } 100 | -------------------------------------------------------------------------------- /examples/tx_multipart_ucs2_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | smpp "github.com/mergenchik/smpp34" 7 | gsmutil "github.com/mergenchik/smpp34/gsmutil" 8 | "math" 9 | ) 10 | 11 | func main() { 12 | // connect and bind 13 | tx, err := smpp.NewTransmitter( 14 | "localhost", 15 | 9000, 16 | 5, 17 | smpp.Params{ 18 | "system_type": "CMT", 19 | "system_id": "hugo", 20 | "password": "ggoohu", 21 | }, 22 | ) 23 | if err != nil { 24 | fmt.Println("Connection Err:", err) 25 | return 26 | } 27 | msg := "Very Long Message, Очень длинное сообщение, 1234567890123456789012345678901234567890123456789012345678901234567890END" 28 | sm_bytes := gsmutil.EncodeUcs2(msg) 29 | sm_len := len(sm_bytes) 30 | fmt.Println("Message Bytes count:", sm_len) 31 | 32 | if sm_len > 140 { 33 | total_parts := byte(int(math.Ceil(float64(sm_len) / 134.0))) 34 | send_params := smpp.Params{smpp.DATA_CODING: smpp.ENCODING_ISO10646, smpp.ESM_CLASS: smpp.ESM_CLASS_GSMFEAT_UDHI} 35 | partNum := 1 36 | uid := make([]byte, 1) 37 | _, err := rand.Read(uid) 38 | if err != nil { 39 | // fmt.Println("QuerySM error:", err) 40 | fmt.Println("Rand.Read error:", err) 41 | return 42 | } 43 | for i := 0; i < sm_len; i += 134 { 44 | start := i 45 | end := i + 134 46 | if end > sm_len { 47 | end = sm_len 48 | } 49 | part := []byte{0x05, 0x00, 0x03, uid[0], total_parts, byte(partNum)} 50 | part = append(part, sm_bytes[start:end]...) 51 | fmt.Println("Part:", part) 52 | // Send SubmitSm 53 | seq, err := tx.SubmitSmEncoded("test", "test2", part, &send_params) 54 | // Pdu gen errors 55 | if err != nil { 56 | fmt.Println("SubmitSm err:", err) 57 | } 58 | // Should save this to match with message_id 59 | fmt.Println("seq:", seq) 60 | partNum++ 61 | 62 | } 63 | 64 | } else { 65 | send_params := smpp.Params{} 66 | // Send SubmitSm 67 | seq, err := tx.SubmitSm("test", "test2", msg, &send_params) 68 | 69 | // Pdu gen errors 70 | if err != nil { 71 | fmt.Println("SubmitSm err:", err) 72 | } 73 | // Should save this to match with message_id 74 | fmt.Println("seq:", seq) 75 | 76 | } 77 | 78 | for { 79 | pdu, err := tx.Read() // This is blocking 80 | if err != nil { 81 | fmt.Println("Read Err:", err) 82 | break 83 | } 84 | 85 | // EnquireLinks are auto handles 86 | switch pdu.GetHeader().Id { 87 | case smpp.SUBMIT_SM_RESP: 88 | // message_id should match this with seq message 89 | fmt.Println("MSG ID:", pdu.GetField("message_id").Value()) 90 | fmt.Printf("PDU Header: %v", pdu.GetHeader()) 91 | fmt.Println() 92 | default: 93 | // ignore all other PDUs or do what you link with them 94 | fmt.Println("PDU ID:", pdu.GetHeader().Id) 95 | } 96 | } 97 | 98 | fmt.Println("ending...") 99 | } 100 | -------------------------------------------------------------------------------- /gsmutil/ucs2.go: -------------------------------------------------------------------------------- 1 | package gsmutil 2 | 3 | /* The MIT License (MIT) 4 | Copyright © 2016 Maxim Kupriianov 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the “Software”), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | // from https://github.com/xlab/at/blob/master/pdu/ucs2.go 26 | import ( 27 | "errors" 28 | "unicode/utf16" 29 | ) 30 | 31 | // ErrUnevenNumber happens when the number of octets (bytes) in the input is uneven. 32 | var ErrUnevenNumber = errors.New("decode ucs2: uneven number of octets") 33 | 34 | // EncodeUcs2 encodes the given UTF-8 text into UCS2 (UTF-16) encoding and returns the produced octets. 35 | func EncodeUcs2(str string) []byte { 36 | buf := utf16.Encode([]rune(str)) 37 | octets := make([]byte, 0, len(buf)*2) 38 | for _, n := range buf { 39 | octets = append(octets, byte(n&0xFF00>>8), byte(n&0x00FF)) 40 | } 41 | return octets 42 | } 43 | 44 | // DecodeUcs2 decodes the given UCS2 (UTF-16) octet data into a UTF-8 encoded string. 45 | func DecodeUcs2(octets []byte) (str string, err error) { 46 | if len(octets)%2 != 0 { 47 | err = ErrUnevenNumber 48 | return 49 | } 50 | buf := make([]uint16, 0, len(octets)/2) 51 | for i := 0; i < len(octets); i += 2 { 52 | buf = append(buf, uint16(octets[i])<<8|uint16(octets[i+1])) 53 | } 54 | runes := utf16.Decode(buf) 55 | return string(runes), nil 56 | } 57 | -------------------------------------------------------------------------------- /pdu.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "reflect" 8 | ) 9 | 10 | const ( 11 | PduLenErr PduReadErr = "Invalid PDU length" 12 | ) 13 | 14 | type PduReadErr string 15 | type PduCmdIdErr string 16 | 17 | type Pdu interface { 18 | Fields() map[string]Field 19 | MandatoryFieldsList() []string 20 | GetField(string) Field 21 | GetHeader() *Header 22 | TLVFields() map[uint16]*TLVField 23 | Writer() []byte 24 | SetField(f string, v interface{}) error 25 | SetTLVField(t, l int, v []byte) error 26 | SetSeqNum(uint32) 27 | Ok() bool 28 | } 29 | 30 | func (p PduReadErr) Error() string { 31 | return string(p) 32 | } 33 | 34 | func (p PduCmdIdErr) Error() string { 35 | return string(p) 36 | } 37 | 38 | func ParsePdu(data []byte) (Pdu, error) { 39 | if len(data) < 16 { 40 | return nil, PduLenErr 41 | } 42 | 43 | header := ParsePduHeader(data[:16]) 44 | 45 | if int(header.Length) != len(data) { 46 | return nil, PduLenErr 47 | } 48 | 49 | switch header.Id { 50 | case SUBMIT_SM: 51 | n, err := NewSubmitSm(header, data[16:]) 52 | return Pdu(n), err 53 | case SUBMIT_SM_RESP: 54 | n, err := NewSubmitSmResp(header, data[16:]) 55 | return Pdu(n), err 56 | case DELIVER_SM: 57 | n, err := NewDeliverSm(header, data[16:]) 58 | return Pdu(n), err 59 | case DELIVER_SM_RESP: 60 | n, err := NewDeliverSmResp(header, data[16:]) 61 | return Pdu(n), err 62 | case BIND_TRANSCEIVER, BIND_RECEIVER, BIND_TRANSMITTER: 63 | n, err := NewBind(header, data[16:]) 64 | return Pdu(n), err 65 | case BIND_TRANSCEIVER_RESP, BIND_RECEIVER_RESP, BIND_TRANSMITTER_RESP: 66 | n, err := NewBindResp(header, data[16:]) 67 | return Pdu(n), err 68 | case ENQUIRE_LINK: 69 | n, err := NewEnquireLink(header) 70 | return Pdu(n), err 71 | case ENQUIRE_LINK_RESP: 72 | n, err := NewEnquireLinkResp(header) 73 | return Pdu(n), err 74 | case UNBIND: 75 | n, err := NewUnbind(header) 76 | return Pdu(n), err 77 | case UNBIND_RESP: 78 | n, err := NewUnbindResp(header) 79 | return Pdu(n), err 80 | case GENERIC_NACK: 81 | n, err := NewGenericNack(header) 82 | return Pdu(n), err 83 | case QUERY_SM: 84 | n, err := NewQuerySm(header, data[16:]) 85 | return Pdu(n), err 86 | case QUERY_SM_RESP: 87 | n, err := NewQuerySmResp(header, data[16:]) 88 | return Pdu(n), err 89 | default: 90 | return nil, PduCmdIdErr(header.Id.Error()) 91 | } 92 | } 93 | 94 | func ParsePduHeader(data []byte) *Header { 95 | return NewPduHeader( 96 | unpackUi32(data[:4]), 97 | CMDId(unpackUi32(data[4:8])), 98 | CMDStatus(unpackUi32(data[8:12])), 99 | unpackUi32(data[12:16]), 100 | ) 101 | } 102 | 103 | func create_pdu_fields(fieldNames []string, r *bytes.Buffer) (map[string]Field, map[uint16]*TLVField, error) { 104 | 105 | fields := make(map[string]Field) 106 | eof := false 107 | for _, k := range fieldNames { 108 | switch k { 109 | case SERVICE_TYPE, SOURCE_ADDR, DESTINATION_ADDR, SCHEDULE_DELIVERY_TIME, VALIDITY_PERIOD, SYSTEM_ID, PASSWORD, SYSTEM_TYPE, ADDRESS_RANGE, MESSAGE_ID, FINAL_DATE, MESSAGE_STATE, ERROR_CODE: 110 | // Review this for fields that could be 1 or 17 int in length (E.g: FINAL_DATE) 111 | t, err := r.ReadBytes(0x00) 112 | 113 | if err == io.EOF { 114 | eof = true 115 | } else if err != nil { 116 | return nil, nil, err 117 | } 118 | 119 | if len(t) == 0 { 120 | fields[k] = NewVariableField(t) 121 | } else { 122 | fields[k] = NewVariableField(t[:len(t)-1]) 123 | } 124 | case SOURCE_ADDR_TON, SOURCE_ADDR_NPI, DEST_ADDR_TON, DEST_ADDR_NPI, ESM_CLASS, PROTOCOL_ID, PRIORITY_FLAG, REGISTERED_DELIVERY, REPLACE_IF_PRESENT_FLAG, DATA_CODING, SM_DEFAULT_MSG_ID, INTERFACE_VERSION, ADDR_TON, ADDR_NPI: 125 | t, err := r.ReadByte() 126 | 127 | if err == io.EOF { 128 | eof = true 129 | } else if err != nil { 130 | return nil, nil, err 131 | } 132 | 133 | fields[k] = NewFixedField(t) 134 | case SM_LENGTH: 135 | // Short Message Length 136 | t, err := r.ReadByte() 137 | 138 | if err == io.EOF { 139 | eof = true 140 | } else if err != nil { 141 | return nil, nil, err 142 | } 143 | 144 | fields[k] = NewFixedField(t) 145 | 146 | // Short Message 147 | p := make([]byte, t) 148 | 149 | _, err = r.Read(p) 150 | if err == io.EOF { 151 | eof = true 152 | } else if err != nil { 153 | return nil, nil, err 154 | } 155 | 156 | fields[SHORT_MESSAGE] = NewSMField(p) 157 | case SHORT_MESSAGE: 158 | continue 159 | } 160 | } 161 | 162 | // Optional Fields 163 | tlvs := map[uint16]*TLVField{} 164 | var err error 165 | 166 | if !eof { 167 | tlvs, err = parse_tlv_fields(r) 168 | 169 | if err != nil { 170 | return nil, nil, err 171 | } 172 | } 173 | 174 | return fields, tlvs, nil 175 | } 176 | 177 | func parse_tlv_fields(r *bytes.Buffer) (map[uint16]*TLVField, error) { 178 | tlvs := map[uint16]*TLVField{} 179 | 180 | for { 181 | p := make([]byte, 4) 182 | _, err := r.Read(p) 183 | 184 | if err == io.EOF { 185 | break 186 | } else if err != nil { 187 | return nil, err 188 | } 189 | 190 | // length 191 | l := unpackUi16(p[2:4]) 192 | 193 | // Get Value 194 | v := make([]byte, l) 195 | 196 | _, err = r.Read(v) 197 | if err != nil { 198 | return nil, err 199 | } 200 | 201 | tlvs[unpackUi16(p[0:2])] = &TLVField{ 202 | unpackUi16(p[0:2]), 203 | unpackUi16(p[2:4]), 204 | v, 205 | } 206 | } 207 | 208 | return tlvs, nil 209 | } 210 | 211 | func validate_pdu_field(f string, v interface{}) bool { 212 | switch f { 213 | case SOURCE_ADDR_TON, SOURCE_ADDR_NPI, DEST_ADDR_TON, DEST_ADDR_NPI, ESM_CLASS, PROTOCOL_ID, PRIORITY_FLAG, REGISTERED_DELIVERY, REPLACE_IF_PRESENT_FLAG, DATA_CODING, SM_DEFAULT_MSG_ID, INTERFACE_VERSION, ADDR_TON, ADDR_NPI, SM_LENGTH, MESSAGE_STATE, ERROR_CODE: 214 | if validate_pdu_field_type(0x00, v) { 215 | return true 216 | } 217 | case SERVICE_TYPE, SOURCE_ADDR, DESTINATION_ADDR, SCHEDULE_DELIVERY_TIME, VALIDITY_PERIOD, SYSTEM_ID, PASSWORD, SYSTEM_TYPE, ADDRESS_RANGE, MESSAGE_ID, SHORT_MESSAGE, FINAL_DATE: 218 | if validate_pdu_field_type("string", v) { 219 | return true 220 | } else if f == SHORT_MESSAGE && validate_pdu_field_type([]byte{0x00}, v) { 221 | return true 222 | } 223 | } 224 | return false 225 | } 226 | 227 | func validate_pdu_field_type(t interface{}, v interface{}) bool { 228 | if reflect.TypeOf(t) == reflect.TypeOf(v) { 229 | return true 230 | } 231 | 232 | return false 233 | } 234 | 235 | func included_check(a []string, v string) bool { 236 | for _, k := range a { 237 | if k == v { 238 | return true 239 | } 240 | } 241 | return false 242 | } 243 | 244 | func unpackUi32(b []byte) (n uint32) { 245 | n = binary.BigEndian.Uint32(b) 246 | return 247 | } 248 | 249 | func packUi32(n uint32) (b []byte) { 250 | b = make([]byte, 4) 251 | binary.BigEndian.PutUint32(b, n) 252 | return 253 | } 254 | 255 | func unpackUi16(b []byte) (n uint16) { 256 | n = binary.BigEndian.Uint16(b) 257 | return 258 | } 259 | 260 | func packUi16(n uint16) (b []byte) { 261 | b = make([]byte, 2) 262 | binary.BigEndian.PutUint16(b, n) 263 | return 264 | } 265 | 266 | func packUi8(n uint8) (b []byte) { 267 | b = make([]byte, 2) 268 | binary.BigEndian.PutUint16(b, uint16(n)) 269 | return b[1:] 270 | } 271 | -------------------------------------------------------------------------------- /pdu_bind.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | var ( 8 | reqBindFields = []string{ 9 | SYSTEM_ID, 10 | PASSWORD, 11 | SYSTEM_TYPE, 12 | INTERFACE_VERSION, 13 | ADDR_TON, 14 | ADDR_NPI, 15 | ADDRESS_RANGE, 16 | } 17 | ) 18 | 19 | type Bind struct { 20 | *Header 21 | mandatoryFields map[string]Field 22 | tlvFields map[uint16]*TLVField 23 | } 24 | 25 | func NewBind(hdr *Header, b []byte) (*Bind, error) { 26 | r := bytes.NewBuffer(b) 27 | 28 | fields, _, err := create_pdu_fields(reqBindFields, r) 29 | 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | s := &Bind{Header: hdr, mandatoryFields: fields} 35 | 36 | return s, nil 37 | } 38 | 39 | func (s *Bind) GetField(f string) Field { 40 | return s.mandatoryFields[f] 41 | } 42 | 43 | func (s *Bind) Fields() map[string]Field { 44 | return s.mandatoryFields 45 | } 46 | 47 | func (s *Bind) MandatoryFieldsList() []string { 48 | return reqBindFields 49 | } 50 | 51 | func (s *Bind) Ok() bool { 52 | return true 53 | } 54 | 55 | func (s *Bind) GetHeader() *Header { 56 | return s.Header 57 | } 58 | 59 | func (s *Bind) SetField(f string, v interface{}) error { 60 | if s.validate_field(f, v) { 61 | field := NewField(f, v) 62 | 63 | if field != nil { 64 | s.mandatoryFields[f] = field 65 | 66 | return nil 67 | } 68 | } 69 | 70 | return FieldValueErr 71 | } 72 | 73 | func (s *Bind) SetSeqNum(i uint32) { 74 | s.Header.Sequence = i 75 | } 76 | 77 | func (s *Bind) SetTLVField(t, l int, v []byte) error { 78 | return TLVFieldPduErr 79 | } 80 | 81 | func (s *Bind) validate_field(f string, v interface{}) bool { 82 | if included_check(s.MandatoryFieldsList(), f) && validate_pdu_field(f, v) { 83 | return true 84 | } 85 | return false 86 | } 87 | 88 | func (s *Bind) TLVFields() map[uint16]*TLVField { 89 | return s.tlvFields 90 | } 91 | 92 | func (s *Bind) writeFields() []byte { 93 | b := []byte{} 94 | 95 | for _, i := range s.MandatoryFieldsList() { 96 | v := s.mandatoryFields[i].ByteArray() 97 | b = append(b, v...) 98 | } 99 | 100 | return b 101 | } 102 | 103 | func (s *Bind) Writer() []byte { 104 | b := s.writeFields() 105 | h := packUi32(uint32(len(b) + 16)) 106 | h = append(h, packUi32(uint32(s.Header.Id))...) 107 | h = append(h, packUi32(uint32(s.Header.Status))...) 108 | h = append(h, packUi32(s.Header.Sequence)...) 109 | return append(h, b...) 110 | } 111 | -------------------------------------------------------------------------------- /pdu_bind_resp.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | var ( 8 | reqBindRespFields = []string{SYSTEM_ID} 9 | ) 10 | 11 | type BindResp struct { 12 | *Header 13 | mandatoryFields map[string]Field 14 | tlvFields map[uint16]*TLVField 15 | } 16 | 17 | func NewBindResp(hdr *Header, b []byte) (*BindResp, error) { 18 | r := bytes.NewBuffer(b) 19 | 20 | fields, tlvs, err := create_pdu_fields(reqBindRespFields, r) 21 | 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | s := &BindResp{hdr, fields, tlvs} 27 | 28 | return s, nil 29 | } 30 | 31 | func (s *BindResp) GetField(f string) Field { 32 | return s.mandatoryFields[f] 33 | } 34 | 35 | func (s *BindResp) Fields() map[string]Field { 36 | return s.mandatoryFields 37 | } 38 | 39 | func (s *BindResp) MandatoryFieldsList() []string { 40 | return reqBindRespFields 41 | } 42 | 43 | func (s *BindResp) Ok() bool { 44 | if s.Header.Status == ESME_ROK { 45 | return true 46 | } 47 | 48 | return false 49 | } 50 | 51 | func (s *BindResp) GetHeader() *Header { 52 | return s.Header 53 | } 54 | 55 | func (s *BindResp) SetField(f string, v interface{}) error { 56 | if s.validate_field(f, v) { 57 | field := NewField(f, v) 58 | 59 | if field != nil { 60 | s.mandatoryFields[f] = field 61 | 62 | return nil 63 | } 64 | } 65 | 66 | return FieldValueErr 67 | } 68 | 69 | func (s *BindResp) SetSeqNum(i uint32) { 70 | s.Header.Sequence = i 71 | } 72 | 73 | func (s *BindResp) SetTLVField(t, l int, v []byte) error { 74 | if l != len(v) { 75 | return TLVFieldLenErr 76 | } 77 | 78 | s.tlvFields[uint16(t)] = &TLVField{uint16(t), uint16(l), v} 79 | 80 | return nil 81 | } 82 | 83 | func (s *BindResp) validate_field(f string, v interface{}) bool { 84 | if included_check(s.MandatoryFieldsList(), f) && validate_pdu_field(f, v) { 85 | return true 86 | } 87 | return false 88 | } 89 | 90 | func (s *BindResp) TLVFields() map[uint16]*TLVField { 91 | return s.tlvFields 92 | } 93 | 94 | func (s *BindResp) writeFields() []byte { 95 | b := []byte{} 96 | 97 | for _, i := range s.MandatoryFieldsList() { 98 | v := s.mandatoryFields[i].ByteArray() 99 | b = append(b, v...) 100 | } 101 | 102 | return b 103 | } 104 | 105 | func (s *BindResp) writeTLVFields() []byte { 106 | b := []byte{} 107 | 108 | for _, v := range s.tlvFields { 109 | b = append(b, v.Writer()...) 110 | } 111 | 112 | return b 113 | } 114 | 115 | func (s *BindResp) Writer() []byte { 116 | b := append(s.writeFields(), s.writeTLVFields()...) 117 | h := packUi32(uint32(len(b) + 16)) 118 | h = append(h, packUi32(uint32(s.Header.Id))...) 119 | h = append(h, packUi32(uint32(s.Header.Status))...) 120 | h = append(h, packUi32(s.Header.Sequence)...) 121 | return append(h, b...) 122 | } 123 | -------------------------------------------------------------------------------- /pdu_deliver_sm.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | var ( 8 | reqDSMFields = []string{ 9 | SERVICE_TYPE, 10 | SOURCE_ADDR_TON, 11 | SOURCE_ADDR_NPI, 12 | SOURCE_ADDR, 13 | DEST_ADDR_TON, 14 | DEST_ADDR_NPI, 15 | DESTINATION_ADDR, 16 | ESM_CLASS, 17 | PROTOCOL_ID, 18 | PRIORITY_FLAG, 19 | SCHEDULE_DELIVERY_TIME, 20 | VALIDITY_PERIOD, 21 | REGISTERED_DELIVERY, 22 | REPLACE_IF_PRESENT_FLAG, 23 | DATA_CODING, 24 | SM_DEFAULT_MSG_ID, 25 | SM_LENGTH, 26 | SHORT_MESSAGE, 27 | } 28 | ) 29 | 30 | type DeliverSm struct { 31 | *Header 32 | mandatoryFields map[string]Field 33 | tlvFields map[uint16]*TLVField 34 | } 35 | 36 | func NewDeliverSm(hdr *Header, b []byte) (*DeliverSm, error) { 37 | r := bytes.NewBuffer(b) 38 | 39 | fields, tlvs, err := create_pdu_fields(reqDSMFields, r) 40 | 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | d := &DeliverSm{hdr, fields, tlvs} 46 | 47 | return d, nil 48 | } 49 | 50 | func (d *DeliverSm) GetField(f string) Field { 51 | return d.mandatoryFields[f] 52 | } 53 | 54 | func (d *DeliverSm) Fields() map[string]Field { 55 | return d.mandatoryFields 56 | } 57 | 58 | func (d *DeliverSm) MandatoryFieldsList() []string { 59 | return reqDSMFields 60 | } 61 | 62 | func (d *DeliverSm) Ok() bool { 63 | return true 64 | } 65 | 66 | func (d *DeliverSm) GetHeader() *Header { 67 | return d.Header 68 | } 69 | 70 | func (d *DeliverSm) SetField(f string, v interface{}) error { 71 | if d.validate_field(f, v) { 72 | field := NewField(f, v) 73 | 74 | if field != nil { 75 | d.mandatoryFields[f] = field 76 | 77 | return nil 78 | } 79 | } 80 | 81 | return FieldValueErr 82 | } 83 | 84 | func (d *DeliverSm) SetSeqNum(i uint32) { 85 | d.Header.Sequence = i 86 | } 87 | 88 | func (d *DeliverSm) SetTLVField(t, l int, v []byte) error { 89 | if l != len(v) { 90 | return TLVFieldLenErr 91 | } 92 | 93 | d.tlvFields[uint16(t)] = &TLVField{uint16(t), uint16(l), v} 94 | 95 | return nil 96 | } 97 | 98 | func (d *DeliverSm) validate_field(f string, v interface{}) bool { 99 | if included_check(d.MandatoryFieldsList(), f) && validate_pdu_field(f, v) { 100 | return true 101 | } 102 | return false 103 | } 104 | 105 | func (d *DeliverSm) TLVFields() map[uint16]*TLVField { 106 | return d.tlvFields 107 | } 108 | 109 | func (d *DeliverSm) writeFields() []byte { 110 | b := []byte{} 111 | 112 | for _, i := range d.MandatoryFieldsList() { 113 | v := d.mandatoryFields[i].ByteArray() 114 | b = append(b, v...) 115 | } 116 | 117 | return b 118 | } 119 | 120 | func (d *DeliverSm) writeTLVFields() []byte { 121 | b := []byte{} 122 | 123 | for _, v := range d.tlvFields { 124 | b = append(b, v.Writer()...) 125 | } 126 | 127 | return b 128 | } 129 | 130 | func (d *DeliverSm) Writer() []byte { 131 | // SM_LENGTH 132 | sm := len(d.GetField(SHORT_MESSAGE).ByteArray()) 133 | d.SetField(SM_LENGTH, sm) 134 | 135 | b := append(d.writeFields(), d.writeTLVFields()...) 136 | h := packUi32(uint32(len(b) + 16)) 137 | h = append(h, packUi32(uint32(DELIVER_SM))...) 138 | h = append(h, packUi32(uint32(d.Header.Status))...) 139 | h = append(h, packUi32(d.Header.Sequence)...) 140 | 141 | return append(h, b...) 142 | } 143 | -------------------------------------------------------------------------------- /pdu_deliver_sm_resp.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | var ( 8 | reqDSMRespFields = []string{MESSAGE_ID} 9 | ) 10 | 11 | type DeliverSmResp struct { 12 | *Header 13 | mandatoryFields map[string]Field 14 | tlvFields map[uint16]*TLVField 15 | } 16 | 17 | func NewDeliverSmResp(hdr *Header, b []byte) (*DeliverSmResp, error) { 18 | r := bytes.NewBuffer(b) 19 | 20 | fields, _, err := create_pdu_fields(reqDSMRespFields, r) 21 | 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | s := &DeliverSmResp{Header: hdr, mandatoryFields: fields} 27 | 28 | return s, nil 29 | } 30 | 31 | func (s *DeliverSmResp) GetField(f string) Field { 32 | return s.mandatoryFields[f] 33 | } 34 | 35 | func (s *DeliverSmResp) Fields() map[string]Field { 36 | return s.mandatoryFields 37 | } 38 | 39 | func (s *DeliverSmResp) MandatoryFieldsList() []string { 40 | return reqDSMRespFields 41 | } 42 | 43 | func (s *DeliverSmResp) Ok() bool { 44 | if s.Header.Status == ESME_ROK { 45 | return true 46 | } 47 | 48 | return false 49 | } 50 | 51 | func (s *DeliverSmResp) GetHeader() *Header { 52 | return s.Header 53 | } 54 | 55 | func (s *DeliverSmResp) SetField(f string, v interface{}) error { 56 | if s.validate_field(f, v) { 57 | field := NewField(f, v) 58 | 59 | if field != nil { 60 | s.mandatoryFields[f] = field 61 | 62 | return nil 63 | } 64 | } 65 | 66 | return FieldValueErr 67 | } 68 | 69 | func (s *DeliverSmResp) SetSeqNum(i uint32) { 70 | s.Header.Sequence = i 71 | } 72 | 73 | func (s *DeliverSmResp) SetTLVField(t, l int, v []byte) error { 74 | return TLVFieldPduErr 75 | } 76 | 77 | func (s *DeliverSmResp) validate_field(f string, v interface{}) bool { 78 | if included_check(s.MandatoryFieldsList(), f) && validate_pdu_field(f, v) { 79 | return true 80 | } 81 | return false 82 | } 83 | 84 | func (s *DeliverSmResp) TLVFields() map[uint16]*TLVField { 85 | return s.tlvFields 86 | } 87 | 88 | func (s *DeliverSmResp) writeFields() []byte { 89 | b := []byte{} 90 | 91 | for _, i := range s.MandatoryFieldsList() { 92 | v := s.mandatoryFields[i].ByteArray() 93 | b = append(b, v...) 94 | } 95 | 96 | return b 97 | } 98 | 99 | func (s *DeliverSmResp) Writer() []byte { 100 | b := s.writeFields() 101 | h := packUi32(uint32(len(b) + 16)) 102 | h = append(h, packUi32(uint32(DELIVER_SM_RESP))...) 103 | h = append(h, packUi32(uint32(s.Header.Status))...) 104 | h = append(h, packUi32(s.Header.Sequence)...) 105 | return append(h, b...) 106 | } 107 | -------------------------------------------------------------------------------- /pdu_enquire_link.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | var ( 4 | reqELFields = []string{} 5 | ) 6 | 7 | type EnquireLink struct { 8 | *Header 9 | mandatoryFields map[string]Field 10 | tlvFields map[uint16]*TLVField 11 | } 12 | 13 | func NewEnquireLink(hdr *Header) (*EnquireLink, error) { 14 | s := &EnquireLink{Header: hdr} 15 | 16 | return s, nil 17 | } 18 | 19 | func (s *EnquireLink) GetField(f string) Field { 20 | return nil 21 | } 22 | 23 | func (s *EnquireLink) Fields() map[string]Field { 24 | return s.mandatoryFields 25 | } 26 | 27 | func (s *EnquireLink) MandatoryFieldsList() []string { 28 | return reqELFields 29 | } 30 | 31 | func (s *EnquireLink) Ok() bool { 32 | return true 33 | } 34 | 35 | func (s *EnquireLink) GetHeader() *Header { 36 | return s.Header 37 | } 38 | 39 | func (s *EnquireLink) SetField(f string, v interface{}) error { 40 | return FieldValueErr 41 | } 42 | 43 | func (s *EnquireLink) SetSeqNum(i uint32) { 44 | s.Header.Sequence = i 45 | } 46 | 47 | func (s *EnquireLink) SetTLVField(t, l int, v []byte) error { 48 | return TLVFieldPduErr 49 | } 50 | 51 | func (s *EnquireLink) TLVFields() map[uint16]*TLVField { 52 | return s.tlvFields 53 | } 54 | 55 | func (s *EnquireLink) writeFields() []byte { 56 | return []byte{} 57 | } 58 | 59 | func (s *EnquireLink) Writer() []byte { 60 | b := s.writeFields() 61 | h := packUi32(uint32(len(b) + 16)) 62 | h = append(h, packUi32(uint32(ENQUIRE_LINK))...) 63 | h = append(h, packUi32(uint32(s.Header.Status))...) 64 | h = append(h, packUi32(s.Header.Sequence)...) 65 | return append(h, b...) 66 | } 67 | -------------------------------------------------------------------------------- /pdu_enquire_link_resp.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | var ( 4 | reqELRespFields = []string{} 5 | ) 6 | 7 | type EnquireLinkResp struct { 8 | *Header 9 | mandatoryFields map[string]Field 10 | tlvFields map[uint16]*TLVField 11 | } 12 | 13 | func NewEnquireLinkResp(hdr *Header) (*EnquireLinkResp, error) { 14 | s := &EnquireLinkResp{Header: hdr} 15 | 16 | return s, nil 17 | } 18 | 19 | func (s *EnquireLinkResp) GetField(f string) Field { 20 | return nil 21 | } 22 | 23 | func (s *EnquireLinkResp) SetField(f string, v interface{}) error { 24 | return FieldValueErr 25 | } 26 | 27 | func (s *EnquireLinkResp) SetSeqNum(i uint32) { 28 | s.Header.Sequence = i 29 | } 30 | 31 | func (s *EnquireLinkResp) SetTLVField(t, l int, v []byte) error { 32 | return TLVFieldPduErr 33 | } 34 | 35 | func (s *EnquireLinkResp) Fields() map[string]Field { 36 | return s.mandatoryFields 37 | } 38 | 39 | func (s *EnquireLinkResp) MandatoryFieldsList() []string { 40 | return reqELRespFields 41 | } 42 | 43 | func (s *EnquireLinkResp) Ok() bool { 44 | if s.Header.Status == ESME_ROK { 45 | return true 46 | } 47 | 48 | return false 49 | } 50 | 51 | func (s *EnquireLinkResp) GetHeader() *Header { 52 | return s.Header 53 | } 54 | 55 | func (s *EnquireLinkResp) TLVFields() map[uint16]*TLVField { 56 | return s.tlvFields 57 | } 58 | 59 | func (s *EnquireLinkResp) writeFields() []byte { 60 | return []byte{} 61 | } 62 | 63 | func (s *EnquireLinkResp) Writer() []byte { 64 | b := s.writeFields() 65 | h := packUi32(uint32(len(b) + 16)) 66 | h = append(h, packUi32(uint32(ENQUIRE_LINK_RESP))...) 67 | h = append(h, packUi32(uint32(s.Header.Status))...) 68 | h = append(h, packUi32(s.Header.Sequence)...) 69 | return append(h, b...) 70 | } 71 | -------------------------------------------------------------------------------- /pdu_fields.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import "strconv" 4 | 5 | const FieldValueErr FieldErr = "Invalid field value" 6 | 7 | type Field interface { 8 | Length() interface{} 9 | Value() interface{} 10 | String() string 11 | ByteArray() []byte 12 | } 13 | 14 | type FieldErr string 15 | 16 | type SMField struct { 17 | value []byte 18 | } 19 | 20 | type VariableField struct { 21 | value []byte 22 | } 23 | 24 | type FixedField struct { 25 | size uint8 26 | value uint8 27 | } 28 | 29 | func NewField(f string, v interface{}) Field { 30 | switch f { 31 | case SOURCE_ADDR_TON, SOURCE_ADDR_NPI, DEST_ADDR_TON, DEST_ADDR_NPI, ESM_CLASS, PROTOCOL_ID, PRIORITY_FLAG, REGISTERED_DELIVERY, REPLACE_IF_PRESENT_FLAG, DATA_CODING, SM_DEFAULT_MSG_ID, INTERFACE_VERSION, ADDR_TON, ADDR_NPI, SM_LENGTH, MESSAGE_STATE, ERROR_CODE: 32 | return NewFixedField(uint8(v.(int))) 33 | case SERVICE_TYPE, SOURCE_ADDR, DESTINATION_ADDR, SCHEDULE_DELIVERY_TIME, VALIDITY_PERIOD, SYSTEM_ID, PASSWORD, SYSTEM_TYPE, ADDRESS_RANGE, MESSAGE_ID, FINAL_DATE: 34 | return NewVariableField([]byte(v.(string))) 35 | case SHORT_MESSAGE: 36 | switch v.(type) { 37 | case []byte: 38 | return NewSMField(v.([]byte)) 39 | default: 40 | return NewSMField([]byte(v.(string))) 41 | } 42 | 43 | } 44 | return nil 45 | } 46 | 47 | func NewSMField(v []byte) Field { 48 | i := &SMField{v} 49 | f := Field(i) 50 | return f 51 | } 52 | 53 | func NewVariableField(v []byte) Field { 54 | i := &VariableField{v} 55 | f := Field(i) 56 | return f 57 | } 58 | 59 | func NewFixedField(v uint8) Field { 60 | i := &FixedField{1, v} 61 | f := Field(i) 62 | return f 63 | } 64 | 65 | func (v *VariableField) Length() interface{} { 66 | l := len(v.value) 67 | return l 68 | } 69 | 70 | func (v *VariableField) Value() interface{} { 71 | return v.value 72 | } 73 | 74 | func (v *VariableField) String() string { 75 | return string(v.value) 76 | } 77 | 78 | func (v *VariableField) ByteArray() []byte { 79 | return append(v.value, 0x00) 80 | } 81 | 82 | func (f *FixedField) Length() interface{} { 83 | return uint8(1) 84 | } 85 | 86 | func (f *FixedField) Value() interface{} { 87 | return f.value 88 | } 89 | 90 | func (f *FixedField) String() string { 91 | return strconv.Itoa(int(f.value)) 92 | } 93 | 94 | func (f *FixedField) ByteArray() []byte { 95 | return packUi8(f.value) 96 | } 97 | 98 | func (f FieldErr) Error() string { 99 | return string(f) 100 | } 101 | 102 | func (v *SMField) Length() interface{} { 103 | l := len(v.value) 104 | return l 105 | } 106 | 107 | func (v *SMField) Value() interface{} { 108 | return v.value 109 | } 110 | 111 | func (v *SMField) String() string { 112 | return string(v.value) 113 | } 114 | 115 | func (v *SMField) ByteArray() []byte { 116 | return v.value 117 | } 118 | -------------------------------------------------------------------------------- /pdu_generic_nack.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | var ( 4 | reqGNFields = []string{} 5 | ) 6 | 7 | type GenericNack struct { 8 | *Header 9 | mandatoryFields map[string]Field 10 | tlvFields map[uint16]*TLVField 11 | } 12 | 13 | func NewGenericNack(hdr *Header) (*GenericNack, error) { 14 | s := &GenericNack{Header: hdr} 15 | 16 | return s, nil 17 | } 18 | 19 | func (s *GenericNack) GetField(f string) Field { 20 | return nil 21 | } 22 | 23 | func (s *GenericNack) Fields() map[string]Field { 24 | return s.mandatoryFields 25 | } 26 | 27 | func (s *GenericNack) MandatoryFieldsList() []string { 28 | return reqGNFields 29 | } 30 | 31 | func (s *GenericNack) Ok() bool { 32 | return true 33 | } 34 | 35 | func (s *GenericNack) GetHeader() *Header { 36 | return s.Header 37 | } 38 | 39 | func (s *GenericNack) SetField(f string, v interface{}) error { 40 | return FieldValueErr 41 | } 42 | 43 | func (s *GenericNack) SetSeqNum(i uint32) { 44 | s.Header.Sequence = i 45 | } 46 | 47 | func (s *GenericNack) SetTLVField(t, l int, v []byte) error { 48 | return TLVFieldPduErr 49 | } 50 | 51 | func (s *GenericNack) TLVFields() map[uint16]*TLVField { 52 | return s.tlvFields 53 | } 54 | 55 | func (s *GenericNack) writeFields() []byte { 56 | return []byte{} 57 | } 58 | 59 | func (s *GenericNack) Writer() []byte { 60 | b := s.writeFields() 61 | h := packUi32(uint32(len(b) + 16)) 62 | h = append(h, packUi32(uint32(GENERIC_NACK))...) 63 | h = append(h, packUi32(uint32(s.Header.Status))...) 64 | h = append(h, packUi32(s.Header.Sequence)...) 65 | return append(h, b...) 66 | } 67 | -------------------------------------------------------------------------------- /pdu_header.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import "fmt" 4 | 5 | type CMDStatus uint32 6 | 7 | type CMDId uint32 8 | 9 | type Header struct { 10 | Length uint32 11 | Id CMDId 12 | Status CMDStatus 13 | Sequence uint32 14 | } 15 | 16 | func NewPduHeader(l uint32, id CMDId, status CMDStatus, seq uint32) *Header { 17 | return &Header{l, id, status, seq} 18 | } 19 | 20 | func (s CMDId) Error() string { 21 | switch s { 22 | case GENERIC_NACK: 23 | return fmt.Sprint("GENERIC_NACK") 24 | case BIND_RECEIVER: 25 | return fmt.Sprint("BIND_RECEIVER") 26 | case BIND_RECEIVER_RESP: 27 | return fmt.Sprint("BIND_RECEIVER_RESP") 28 | case BIND_TRANSMITTER: 29 | return fmt.Sprint("BIND_TRANSMITTER") 30 | case BIND_TRANSMITTER_RESP: 31 | return fmt.Sprint("BIND_TRANSMITTER_RESP") 32 | case QUERY_SM: 33 | return fmt.Sprint("QUERY_SM") 34 | case QUERY_SM_RESP: 35 | return fmt.Sprint("QUERY_SM_RESP") 36 | case SUBMIT_SM: 37 | return fmt.Sprint("SUBMIT_SM") 38 | case SUBMIT_SM_RESP: 39 | return fmt.Sprint("SUBMIT_SM_RESP") 40 | case DELIVER_SM: 41 | return fmt.Sprint("DELIVER_SM") 42 | case DELIVER_SM_RESP: 43 | return fmt.Sprint("DELIVER_SM_RESP") 44 | case UNBIND: 45 | return fmt.Sprint("UNBIND") 46 | case UNBIND_RESP: 47 | return fmt.Sprint("UNBIND_RESP") 48 | case REPLACE_SM: 49 | return fmt.Sprint("REPLACE_SM") 50 | case REPLACE_SM_RESP: 51 | return fmt.Sprint("REPLACE_SM_RESP") 52 | case CANCEL_SM: 53 | return fmt.Sprint("CANCEL_SM") 54 | case CANCEL_SM_RESP: 55 | return fmt.Sprint("CANCEL_SM_RESP") 56 | case BIND_TRANSCEIVER: 57 | return fmt.Sprint("BIND_TRANSCEIVER") 58 | case BIND_TRANSCEIVER_RESP: 59 | return fmt.Sprint("BIND_TRANSCEIVER_RESP") 60 | case OUTBIND: 61 | return fmt.Sprint("OUTBIND") 62 | case ENQUIRE_LINK: 63 | return fmt.Sprint("ENQUIRE_LINK") 64 | case ENQUIRE_LINK_RESP: 65 | return fmt.Sprint("ENQUIRE_LINK_RESP") 66 | case SUBMIT_MULTI: 67 | return fmt.Sprint("SUBMIT_MULTI") 68 | case SUBMIT_MULTI_RESP: 69 | return fmt.Sprint("SUBMIT_MULTI_RESP") 70 | case ALERT_NOTIFICATION: 71 | return fmt.Sprint("ALERT_NOTIFICATION") 72 | case DATA_SM: 73 | return fmt.Sprint("DATA_SM") 74 | case DATA_SM_RESP: 75 | return fmt.Sprint("DATA_SM_RESP") 76 | default: 77 | return fmt.Sprint("Unknown PDU Type. ID:", uint32(s)) 78 | } 79 | } 80 | 81 | func (s CMDStatus) Error() string { 82 | switch s { 83 | default: 84 | return fmt.Sprint("Unknown Status:", uint32(s)) 85 | case ESME_ROK: 86 | return fmt.Sprint("No Error") 87 | case ESME_RINVMSGLEN: 88 | return fmt.Sprint("Message Length is invalid") 89 | case ESME_RINVCMDLEN: 90 | return fmt.Sprint("Command Length is invalid") 91 | case ESME_RINVCMDID: 92 | return fmt.Sprint("Invalid Command ID") 93 | case ESME_RINVBNDSTS: 94 | return fmt.Sprint("Incorrect BIND Status for given command") 95 | case ESME_RALYBND: 96 | return fmt.Sprint("ESME Already in Bound State") 97 | case ESME_RINVPRTFLG: 98 | return fmt.Sprint("Invalid Priority Flag") 99 | case ESME_RINVREGDLVFLG: 100 | return fmt.Sprint("Invalid Registered Delivery Flag") 101 | case ESME_RSYSERR: 102 | return fmt.Sprint("System Error") 103 | case ESME_RINVSRCADR: 104 | return fmt.Sprint("Invalid Source Address") 105 | case ESME_RINVDSTADR: 106 | return fmt.Sprint("Invalid Dest Addr") 107 | case ESME_RINVMSGID: 108 | return fmt.Sprint("Message ID is invalid") 109 | case ESME_RBINDFAIL: 110 | return fmt.Sprint("Bind Failed") 111 | case ESME_RINVPASWD: 112 | return fmt.Sprint("Invalid Password") 113 | case ESME_RINVSYSID: 114 | return fmt.Sprint("Invalid System ID") 115 | case ESME_RCANCELFAIL: 116 | return fmt.Sprint("Cancel SM Failed") 117 | case ESME_RREPLACEFAIL: 118 | return fmt.Sprint("Replace SM Failed") 119 | case ESME_RMSGQFUL: 120 | return fmt.Sprint("Message Queue Full") 121 | case ESME_RINVSERTYP: 122 | return fmt.Sprint("Invalid Service Type") 123 | case ESME_RINVNUMDESTS: 124 | return fmt.Sprint("Invalid number of destinations") 125 | case ESME_RINVDLNAME: 126 | return fmt.Sprint("Invalid Distribution List name") 127 | case ESME_RINVDESTFLAG: 128 | return fmt.Sprint("Destination flag is invalid") 129 | case ESME_RINVSUBREP: 130 | return fmt.Sprint("Invalid 'submit with replace' request") 131 | case ESME_RINVESMCLASS: 132 | return fmt.Sprint("Invalid esm_class field data") 133 | case ESME_RCNTSUBDL: 134 | return fmt.Sprint("Cannot Submit to Distribution List") 135 | case ESME_RSUBMITFAIL: 136 | return fmt.Sprint("submit_sm or submit_multi failed") 137 | case ESME_RINVSRCTON: 138 | return fmt.Sprint("Invalid Source address TON") 139 | case ESME_RINVSRCNPI: 140 | return fmt.Sprint("Invalid Source address NPI") 141 | case ESME_RINVDSTTON: 142 | return fmt.Sprint("Invalid Destination address TON") 143 | case ESME_RINVDSTNPI: 144 | return fmt.Sprint("Invalid Destination address NPI") 145 | case ESME_RINVSYSTYP: 146 | return fmt.Sprint("Invalid system_type field") 147 | case ESME_RINVREPFLAG: 148 | return fmt.Sprint("Invalid replace_if_present flag") 149 | case ESME_RINVNUMMSGS: 150 | return fmt.Sprint("Invalid number of messages") 151 | case ESME_RTHROTTLED: 152 | return fmt.Sprint("Throttling error (ESME has exceeded allowed message limit") 153 | case ESME_RINVSCHED: 154 | return fmt.Sprint("Invalid Scheduled Delivery Time") 155 | case ESME_RINVEXPIRY: 156 | return fmt.Sprint("Invalid message validity period (Expiry time)") 157 | case ESME_RINVDFTMSGID: 158 | return fmt.Sprint("Predefined Message Invalid or Not Found") 159 | case ESME_RX_T_APPN: 160 | return fmt.Sprint("ESME Receiver Temporary App Error Code") 161 | case ESME_RX_P_APPN: 162 | return fmt.Sprint("ESME Receiver Permanent App Error Code") 163 | case ESME_RX_R_APPN: 164 | return fmt.Sprint("ESME Receiver Reject Message Error Code") 165 | case ESME_RQUERYFAIL: 166 | return fmt.Sprint("Query_sm request failed") 167 | case ESME_RINVOPTPARSTREAM: 168 | return fmt.Sprint("Error in the optional part of the PDU Body.") 169 | case ESME_ROPTPARNOTALLWD: 170 | return fmt.Sprint("Optional Parameter not allowed") 171 | case ESME_RINVPARLEN: 172 | return fmt.Sprint("Invalid Parameter Length.") 173 | case ESME_RMISSINGOPTPARAM: 174 | return fmt.Sprint("Expected Optional Parameter missing") 175 | case ESME_RINVOPTPARAMVAL: 176 | return fmt.Sprint("Invalid Optional Parameter Value") 177 | case ESME_RDELIVERYFAILURE: 178 | return fmt.Sprint("Delivery Failure (used for data_sm_resp)") 179 | case ESME_RUNKNOWNERR: 180 | return fmt.Sprint("Unknown Error") 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /pdu_query_sm.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | var ( 8 | // Required QuerySm Fields 9 | reqQSMFields = []string{ 10 | MESSAGE_ID, 11 | SOURCE_ADDR_TON, 12 | SOURCE_ADDR_NPI, 13 | SOURCE_ADDR, 14 | } 15 | ) 16 | 17 | type QuerySm struct { 18 | *Header 19 | mandatoryFields map[string]Field 20 | tlvFields map[uint16]*TLVField 21 | } 22 | 23 | func NewQuerySm(hdr *Header, b []byte) (*QuerySm, error) { 24 | r := bytes.NewBuffer(b) 25 | 26 | fields, _, err := create_pdu_fields(reqQSMFields, r) 27 | 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | s := &QuerySm{Header: hdr, mandatoryFields: fields} 33 | 34 | return s, nil 35 | } 36 | 37 | func (s *QuerySm) GetField(f string) Field { 38 | return s.mandatoryFields[f] 39 | } 40 | 41 | func (s *QuerySm) Fields() map[string]Field { 42 | return s.mandatoryFields 43 | } 44 | 45 | func (s *QuerySm) MandatoryFieldsList() []string { 46 | return reqQSMFields 47 | } 48 | 49 | func (s *QuerySm) Ok() bool { 50 | return true 51 | } 52 | 53 | func (s *QuerySm) GetHeader() *Header { 54 | return s.Header 55 | } 56 | 57 | func (s *QuerySm) SetField(f string, v interface{}) error { 58 | if s.validate_field(f, v) { 59 | field := NewField(f, v) 60 | 61 | if field != nil { 62 | s.mandatoryFields[f] = field 63 | 64 | return nil 65 | } 66 | } 67 | 68 | return FieldValueErr 69 | } 70 | 71 | func (s *QuerySm) SetSeqNum(i uint32) { 72 | s.Header.Sequence = i 73 | } 74 | 75 | func (s *QuerySm) SetTLVField(t, l int, v []byte) error { 76 | return TLVFieldPduErr 77 | } 78 | 79 | func (s *QuerySm) validate_field(f string, v interface{}) bool { 80 | if included_check(s.MandatoryFieldsList(), f) && validate_pdu_field(f, v) { 81 | return true 82 | } 83 | return false 84 | } 85 | 86 | func (s *QuerySm) TLVFields() map[uint16]*TLVField { 87 | return s.tlvFields 88 | } 89 | 90 | func (s *QuerySm) writeFields() []byte { 91 | b := []byte{} 92 | 93 | for _, i := range s.MandatoryFieldsList() { 94 | v := s.mandatoryFields[i].ByteArray() 95 | b = append(b, v...) 96 | } 97 | 98 | return b 99 | } 100 | 101 | func (s *QuerySm) Writer() []byte { 102 | b := s.writeFields() 103 | h := packUi32(uint32(len(b) + 16)) 104 | h = append(h, packUi32(uint32(s.Header.Id))...) 105 | h = append(h, packUi32(uint32(s.Header.Status))...) 106 | h = append(h, packUi32(s.Header.Sequence)...) 107 | return append(h, b...) 108 | } 109 | -------------------------------------------------------------------------------- /pdu_query_sm_resp.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | ) 7 | 8 | var ( 9 | // Required QuerySmResp Fields 10 | reqQSMRespFields = []string{ 11 | MESSAGE_ID, 12 | FINAL_DATE, 13 | MESSAGE_STATE, 14 | ERROR_CODE, 15 | } 16 | ) 17 | 18 | type QuerySmResp struct { 19 | *Header 20 | mandatoryFields map[string]Field 21 | tlvFields map[uint16]*TLVField 22 | } 23 | 24 | func NewQuerySmResp(hdr *Header, b []byte) (*QuerySmResp, error) { 25 | r := bytes.NewBuffer(b) 26 | 27 | fields, _, err := create_pdu_fields(reqQSMRespFields, r) 28 | 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | s := &QuerySmResp{Header: hdr, mandatoryFields: fields} 34 | 35 | return s, nil 36 | } 37 | 38 | func (s *QuerySmResp) GetField(f string) Field { 39 | switch f { 40 | case MESSAGE_STATE, ERROR_CODE: 41 | var v uint8 42 | binary.Read(bytes.NewBuffer(s.mandatoryFields[f].ByteArray()), 43 | binary.BigEndian, &v) 44 | return NewFixedField(v) 45 | default: 46 | return s.mandatoryFields[f] 47 | } 48 | } 49 | 50 | func (s *QuerySmResp) Fields() map[string]Field { 51 | return s.mandatoryFields 52 | } 53 | 54 | func (s *QuerySmResp) MandatoryFieldsList() []string { 55 | return reqQSMRespFields 56 | } 57 | 58 | func (s *QuerySmResp) Ok() bool { 59 | return true 60 | } 61 | 62 | func (s *QuerySmResp) GetHeader() *Header { 63 | return s.Header 64 | } 65 | 66 | func (s *QuerySmResp) SetField(f string, v interface{}) error { 67 | if s.validate_field(f, v) { 68 | field := NewField(f, v) 69 | 70 | if field != nil { 71 | s.mandatoryFields[f] = field 72 | 73 | return nil 74 | } 75 | } 76 | 77 | return FieldValueErr 78 | } 79 | 80 | func (s *QuerySmResp) SetSeqNum(i uint32) { 81 | s.Header.Sequence = i 82 | } 83 | 84 | func (s *QuerySmResp) SetTLVField(t, l int, v []byte) error { 85 | return TLVFieldPduErr 86 | } 87 | 88 | func (s *QuerySmResp) validate_field(f string, v interface{}) bool { 89 | if included_check(s.MandatoryFieldsList(), f) && validate_pdu_field(f, v) { 90 | return true 91 | } 92 | return false 93 | } 94 | 95 | func (s *QuerySmResp) TLVFields() map[uint16]*TLVField { 96 | return s.tlvFields 97 | } 98 | 99 | func (s *QuerySmResp) writeFields() []byte { 100 | b := []byte{} 101 | 102 | for _, i := range s.MandatoryFieldsList() { 103 | v := s.mandatoryFields[i].ByteArray() 104 | b = append(b, v...) 105 | } 106 | 107 | return b 108 | } 109 | 110 | func (s *QuerySmResp) Writer() []byte { 111 | b := s.writeFields() 112 | h := packUi32(uint32(len(b) + 16)) 113 | h = append(h, packUi32(uint32(s.Header.Id))...) 114 | h = append(h, packUi32(uint32(s.Header.Status))...) 115 | h = append(h, packUi32(s.Header.Sequence)...) 116 | return append(h, b...) 117 | } 118 | -------------------------------------------------------------------------------- /pdu_submit_sm.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | var ( 8 | // Required SubmitSm Fields 9 | reqSSMFields = []string{ 10 | SERVICE_TYPE, 11 | SOURCE_ADDR_TON, 12 | SOURCE_ADDR_NPI, 13 | SOURCE_ADDR, 14 | DEST_ADDR_TON, 15 | DEST_ADDR_NPI, 16 | DESTINATION_ADDR, 17 | ESM_CLASS, 18 | PROTOCOL_ID, 19 | PRIORITY_FLAG, 20 | SCHEDULE_DELIVERY_TIME, 21 | VALIDITY_PERIOD, 22 | REGISTERED_DELIVERY, 23 | REPLACE_IF_PRESENT_FLAG, 24 | DATA_CODING, 25 | SM_DEFAULT_MSG_ID, 26 | SM_LENGTH, 27 | SHORT_MESSAGE, 28 | } 29 | ) 30 | 31 | type SubmitSm struct { 32 | *Header 33 | mandatoryFields map[string]Field 34 | tlvFields map[uint16]*TLVField 35 | } 36 | 37 | func NewSubmitSm(hdr *Header, b []byte) (*SubmitSm, error) { 38 | r := bytes.NewBuffer(b) 39 | 40 | fields, tlvs, err := create_pdu_fields(reqSSMFields, r) 41 | 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | s := &SubmitSm{hdr, fields, tlvs} 47 | 48 | return s, nil 49 | } 50 | 51 | func (s *SubmitSm) GetField(f string) Field { 52 | return s.mandatoryFields[f] 53 | } 54 | 55 | func (s *SubmitSm) Fields() map[string]Field { 56 | return s.mandatoryFields 57 | } 58 | 59 | func (s *SubmitSm) MandatoryFieldsList() []string { 60 | return reqSSMFields 61 | } 62 | 63 | func (s *SubmitSm) Ok() bool { 64 | return true 65 | } 66 | 67 | func (s *SubmitSm) GetHeader() *Header { 68 | return s.Header 69 | } 70 | 71 | func (s *SubmitSm) SetField(f string, v interface{}) error { 72 | if s.validate_field(f, v) { 73 | field := NewField(f, v) 74 | 75 | if field != nil { 76 | s.mandatoryFields[f] = field 77 | 78 | return nil 79 | } 80 | } 81 | 82 | return FieldValueErr 83 | } 84 | 85 | func (s *SubmitSm) SetSeqNum(i uint32) { 86 | s.Header.Sequence = i 87 | } 88 | 89 | func (s *SubmitSm) SetTLVField(t, l int, v []byte) error { 90 | if l != len(v) { 91 | return TLVFieldLenErr 92 | } 93 | 94 | s.tlvFields[uint16(t)] = &TLVField{uint16(t), uint16(l), v} 95 | 96 | return nil 97 | } 98 | 99 | func (s *SubmitSm) validate_field(f string, v interface{}) bool { 100 | if included_check(s.MandatoryFieldsList(), f) && validate_pdu_field(f, v) { 101 | return true 102 | } 103 | return false 104 | } 105 | 106 | func (s *SubmitSm) TLVFields() map[uint16]*TLVField { 107 | return s.tlvFields 108 | } 109 | 110 | func (s *SubmitSm) writeFields() []byte { 111 | b := []byte{} 112 | 113 | for _, i := range s.MandatoryFieldsList() { 114 | v := s.mandatoryFields[i].ByteArray() 115 | b = append(b, v...) 116 | } 117 | 118 | return b 119 | } 120 | 121 | func (s *SubmitSm) writeTLVFields() []byte { 122 | b := []byte{} 123 | 124 | for _, v := range s.tlvFields { 125 | b = append(b, v.Writer()...) 126 | } 127 | 128 | return b 129 | } 130 | 131 | func (s *SubmitSm) Writer() []byte { 132 | // Set SM_LENGTH 133 | sm := len(s.GetField(SHORT_MESSAGE).ByteArray()) 134 | s.SetField(SM_LENGTH, sm) 135 | 136 | b := append(s.writeFields(), s.writeTLVFields()...) 137 | h := packUi32(uint32(len(b) + 16)) 138 | h = append(h, packUi32(uint32(SUBMIT_SM))...) 139 | h = append(h, packUi32(uint32(s.Header.Status))...) 140 | h = append(h, packUi32(s.Header.Sequence)...) 141 | 142 | return append(h, b...) 143 | } 144 | -------------------------------------------------------------------------------- /pdu_submit_sm_resp.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | var ( 8 | reqSSMRespFields = []string{MESSAGE_ID} 9 | ) 10 | 11 | type SubmitSmResp struct { 12 | *Header 13 | mandatoryFields map[string]Field 14 | tlvFields map[uint16]*TLVField 15 | } 16 | 17 | func NewSubmitSmResp(hdr *Header, b []byte) (*SubmitSmResp, error) { 18 | r := bytes.NewBuffer(b) 19 | 20 | fields, _, err := create_pdu_fields(reqSSMRespFields, r) 21 | 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | s := &SubmitSmResp{Header: hdr, mandatoryFields: fields} 27 | 28 | return s, nil 29 | } 30 | 31 | func (s *SubmitSmResp) GetField(f string) Field { 32 | return s.mandatoryFields[f] 33 | } 34 | 35 | func (s *SubmitSmResp) Fields() map[string]Field { 36 | return s.mandatoryFields 37 | } 38 | 39 | func (s *SubmitSmResp) MandatoryFieldsList() []string { 40 | return reqSSMRespFields 41 | } 42 | 43 | func (s *SubmitSmResp) Ok() bool { 44 | if s.Header.Status == ESME_ROK { 45 | return true 46 | } 47 | 48 | return false 49 | } 50 | 51 | func (s *SubmitSmResp) GetHeader() *Header { 52 | return s.Header 53 | } 54 | 55 | func (s *SubmitSmResp) SetField(f string, v interface{}) error { 56 | if s.validate_field(f, v) { 57 | field := NewField(f, v) 58 | 59 | if field != nil { 60 | s.mandatoryFields[f] = field 61 | 62 | return nil 63 | } 64 | } 65 | 66 | return FieldValueErr 67 | } 68 | 69 | func (s *SubmitSmResp) SetSeqNum(i uint32) { 70 | s.Header.Sequence = i 71 | } 72 | 73 | func (s *SubmitSmResp) SetTLVField(t, l int, v []byte) error { 74 | return TLVFieldPduErr 75 | } 76 | 77 | func (s *SubmitSmResp) validate_field(f string, v interface{}) bool { 78 | if included_check(s.MandatoryFieldsList(), f) && validate_pdu_field(f, v) { 79 | return true 80 | } 81 | return false 82 | } 83 | 84 | func (s *SubmitSmResp) TLVFields() map[uint16]*TLVField { 85 | return s.tlvFields 86 | } 87 | 88 | func (s *SubmitSmResp) writeFields() []byte { 89 | b := []byte{} 90 | 91 | for _, i := range s.MandatoryFieldsList() { 92 | v := s.mandatoryFields[i].ByteArray() 93 | b = append(b, v...) 94 | } 95 | 96 | return b 97 | } 98 | 99 | func (s *SubmitSmResp) Writer() []byte { 100 | b := s.writeFields() 101 | h := packUi32(uint32(len(b) + 16)) 102 | h = append(h, packUi32(uint32(SUBMIT_SM_RESP))...) 103 | h = append(h, packUi32(uint32(s.Header.Status))...) 104 | h = append(h, packUi32(s.Header.Sequence)...) 105 | 106 | return append(h, b...) 107 | } 108 | -------------------------------------------------------------------------------- /pdu_test.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "encoding/hex" 5 | . "launchpad.net/gocheck" 6 | "math/rand" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | const ( 12 | bindPdu = "000000240000000900000000000000016875676f0067676f6f687500434d540034000000" 13 | bindRespPdu = "0000001d80000009000000000000000474657374696e67000210000134" 14 | deliverSmPdu = "0000004d000000050000000052227280000001746573743200010174657374000000010000010000002338393261386563303634633064373639666134353366373762343a2074657374206d6f" 15 | deliverSmRespPdu = "0000001180000005000000005222728000" 16 | enquireLinkPdu = "00000010000000150000000000000005" 17 | enquireLinkRespPdu = "00000010800000150000000000000005" 18 | genericNackPdu = "00000010800000000000000200000000" 19 | submitSmPdu = "0000002d00000004000000000000000200000074657374000000746573743200000000000000000000036d7367" 20 | submitSmRespPdu = "0000003580000004000000005221ac3831303039343665342d356138662d343835642d386536342d65646639616133373761323200" 21 | unbindPdu = "00000010000000060000000000000003" 22 | unbindRespPdu = "00000010800000060000000000000003" 23 | querySmPdu = "0000002000000003000000000000000168656c6c6f00000174657374696e6700" 24 | querySmRespPdu = "0000001a80000003000000000000000168656c6c6f0000010000" 25 | ) 26 | 27 | func Test(t *testing.T) { TestingT(t) } 28 | 29 | type MySuite struct{} 30 | 31 | var _ = Suite(&MySuite{}) 32 | 33 | func (s *MySuite) Test_PduCmdIdErrors(c *C) { 34 | data, _ := hex.DecodeString("00000010900000060000000000000003") 35 | _, err := ParsePdu(data) 36 | c.Check(err.Error(), Equals, "Unknown PDU Type. ID:2415919110") 37 | } 38 | 39 | func (s *MySuite) Test_PduLenErrors(c *C) { 40 | data, _ := hex.DecodeString("000000100000000600000000000000") 41 | _, err := ParsePdu(data) 42 | c.Check(err.Error(), Equals, "Invalid PDU length") 43 | 44 | data, _ = hex.DecodeString("000000F00000000600000000000000") 45 | _, err = ParsePdu(data) 46 | c.Check(err.Error(), Equals, "Invalid PDU length") 47 | } 48 | 49 | func (s *MySuite) Test_BindPdu(c *C) { 50 | data, _ := hex.DecodeString(bindPdu) 51 | p, err := ParsePdu(data) 52 | 53 | c.Check(err, IsNil) 54 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x24, BIND_TRANSCEIVER, ESME_ROK, uint32(1))) 55 | c.Check(p.GetField(SYSTEM_ID).String(), Equals, "hugo") 56 | c.Check(p.GetField(PASSWORD).String(), Equals, "ggoohu") 57 | c.Check(p.GetField(SYSTEM_TYPE).String(), Equals, "CMT") 58 | c.Check(p.GetField(INTERFACE_VERSION).Value(), Equals, uint8(0x34)) 59 | c.Check(p.Writer(), DeepEquals, data) 60 | 61 | // Change values 62 | p.SetField(SYSTEM_ID, "test1") 63 | c.Check(p.GetField(SYSTEM_ID).String(), Equals, "test1") 64 | c.Check(hex.EncodeToString(p.Writer()), DeepEquals, "0000002500000009000000000000000174657374310067676f6f687500434d540034000000") 65 | 66 | c.Check(p.SetTLVField(0x210, 0, []byte{0x1, 0x2}), Equals, TLVFieldPduErr) 67 | c.Check(p.GetField("UNKNOWN_STR"), IsNil) 68 | } 69 | 70 | func (s *MySuite) Test_BindRespPdu(c *C) { 71 | data, _ := hex.DecodeString(bindRespPdu) 72 | p, err := ParsePdu(data) 73 | 74 | c.Check(err, IsNil) 75 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x1d, BIND_TRANSCEIVER_RESP, ESME_ROK, uint32(4))) 76 | c.Check(p.GetField(SYSTEM_ID).String(), Equals, "testing") 77 | c.Check(p.TLVFields()[0x210].Value(), DeepEquals, []uint8{0x34}) 78 | c.Check(p.Writer(), DeepEquals, data) 79 | 80 | c.Check(p.SetTLVField(0x210, 0, []byte{0x1, 0x2}), Equals, TLVFieldLenErr) 81 | c.Check(p.SetTLVField(0x210, 5, []byte{0x1, 0x2}), Equals, TLVFieldLenErr) 82 | } 83 | 84 | func (s *MySuite) Test_DeliverSmPdu(c *C) { 85 | data, _ := hex.DecodeString(deliverSmPdu) 86 | p, err := ParsePdu(data) 87 | 88 | c.Check(err, IsNil) 89 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x4d, DELIVER_SM, ESME_ROK, uint32(0x52227280))) 90 | c.Check(p.Writer(), DeepEquals, data) 91 | 92 | // Change Short Message 93 | p.SetField(SHORT_MESSAGE, "test1") 94 | c.Check(p.GetField(SHORT_MESSAGE).String(), Equals, "test1") 95 | c.Check(hex.EncodeToString(p.Writer()), DeepEquals, "0000002f00000005000000005222728000000174657374320001017465737400000001000001000000057465737431") 96 | } 97 | 98 | func (s *MySuite) Test_DeliverSmRespPdu(c *C) { 99 | data, _ := hex.DecodeString(deliverSmRespPdu) 100 | p, err := ParsePdu(data) 101 | 102 | c.Check(err, IsNil) 103 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x11, DELIVER_SM_RESP, ESME_ROK, uint32(0x52227280))) 104 | c.Check(p.Writer(), DeepEquals, data) 105 | } 106 | 107 | func (s *MySuite) Test_EnquireLinkPdu(c *C) { 108 | data, _ := hex.DecodeString(enquireLinkPdu) 109 | p, err := ParsePdu(data) 110 | 111 | c.Check(err, IsNil) 112 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x10, ENQUIRE_LINK, ESME_ROK, uint32(5))) 113 | c.Check(p.Writer(), DeepEquals, data) 114 | } 115 | 116 | func (s *MySuite) Test_EnquireLinkRespPdu(c *C) { 117 | data, _ := hex.DecodeString(enquireLinkRespPdu) 118 | p, err := ParsePdu(data) 119 | 120 | c.Check(err, IsNil) 121 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x10, ENQUIRE_LINK_RESP, ESME_ROK, uint32(5))) 122 | c.Check(p.Writer(), DeepEquals, data) 123 | } 124 | 125 | func (s *MySuite) Test_GenericNackPdu(c *C) { 126 | data, _ := hex.DecodeString(genericNackPdu) 127 | p, err := ParsePdu(data) 128 | 129 | c.Check(err, IsNil) 130 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x10, GENERIC_NACK, ESME_RINVCMDLEN, uint32(0))) 131 | c.Check(p.Writer(), DeepEquals, data) 132 | } 133 | 134 | func (s *MySuite) Test_SubmitSmPdu(c *C) { 135 | data, _ := hex.DecodeString(submitSmPdu) 136 | p, err := ParsePdu(data) 137 | 138 | c.Check(err, IsNil) 139 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x2d, SUBMIT_SM, ESME_ROK, uint32(2))) 140 | c.Check(p.SetField(SHORT_MESSAGE, 1).Error(), Equals, FieldValueErr.Error()) 141 | c.Check(p.Writer(), DeepEquals, data) 142 | 143 | // Change Short Message 144 | p.SetField(SHORT_MESSAGE, "test1") 145 | c.Check(p.GetField(SHORT_MESSAGE).String(), Equals, "test1") 146 | c.Check(hex.EncodeToString(p.Writer()), DeepEquals, "0000002f00000004000000000000000200000074657374000000746573743200000000000000000000057465737431") 147 | } 148 | 149 | func (s *MySuite) Test_SubmitSmRespPdu(c *C) { 150 | data, _ := hex.DecodeString(submitSmRespPdu) 151 | p, err := ParsePdu(data) 152 | 153 | c.Check(err, IsNil) 154 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x35, SUBMIT_SM_RESP, ESME_ROK, uint32(0x5221ac38))) 155 | c.Check(p.Writer(), DeepEquals, data) 156 | c.Check(p.GetField(MESSAGE_ID).String(), Equals, "100946e4-5a8f-485d-8e64-edf9aa377a22") 157 | } 158 | 159 | func (s *MySuite) Test_UnbindPdu(c *C) { 160 | data, _ := hex.DecodeString(unbindPdu) 161 | p, err := ParsePdu(data) 162 | 163 | c.Check(err, IsNil) 164 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x10, UNBIND, ESME_ROK, uint32(3))) 165 | } 166 | 167 | func (s *MySuite) Test_UnbindRespPdu(c *C) { 168 | data, _ := hex.DecodeString(unbindRespPdu) 169 | p, err := ParsePdu(data) 170 | 171 | c.Check(err, IsNil) 172 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x10, UNBIND_RESP, ESME_ROK, uint32(3))) 173 | } 174 | 175 | func (s *MySuite) Test_QuerySmPdu(c *C) { 176 | data, _ := hex.DecodeString(querySmPdu) 177 | p, err := ParsePdu(data) 178 | 179 | c.Check(err, IsNil) 180 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x20, QUERY_SM, ESME_ROK, uint32(1))) 181 | c.Check(p.GetField(MESSAGE_ID).String(), Equals, "hello") 182 | c.Check(p.GetField(SOURCE_ADDR_TON).Value(), Equals, uint8(0)) 183 | c.Check(p.GetField(SOURCE_ADDR_NPI).Value(), Equals, uint8(1)) 184 | c.Check(p.GetField(SOURCE_ADDR).String(), Equals, "testing") 185 | c.Check(p.Writer(), DeepEquals, data) 186 | } 187 | 188 | func (s *MySuite) Test_QuerySmRespPdu(c *C) { 189 | data, _ := hex.DecodeString(querySmRespPdu) 190 | p, err := ParsePdu(data) 191 | 192 | c.Check(err, IsNil) 193 | c.Check(p.GetHeader(), DeepEquals, NewPduHeader(0x1a, QUERY_SM_RESP, ESME_ROK, uint32(1))) 194 | c.Check(p.GetField(MESSAGE_ID).String(), Equals, "hello") 195 | c.Check(p.GetField(FINAL_DATE).String(), Equals, "") 196 | c.Check(p.Writer(), DeepEquals, data) 197 | } 198 | 199 | func (s *MySuite) BenchmarkPduParsing(c *C) { 200 | c.StopTimer() 201 | pdus := []string{ 202 | bindPdu, 203 | bindRespPdu, 204 | deliverSmPdu, 205 | deliverSmRespPdu, 206 | enquireLinkPdu, 207 | enquireLinkRespPdu, 208 | genericNackPdu, 209 | submitSmPdu, 210 | submitSmRespPdu, 211 | unbindPdu, 212 | unbindRespPdu, 213 | } 214 | 215 | for i := 0; i < c.N; i++ { 216 | p, _ := hex.DecodeString(pdus[rand.Intn(len(pdus))]) 217 | c.StartTimer() 218 | ParsePdu(p) 219 | c.StopTimer() 220 | rand.Seed(time.Now().UTC().UnixNano()) 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /pdu_tlv_field.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | const ( 4 | TLVFieldLenErr TLVFieldErr = "Invalid TLV value lenght" 5 | TLVFieldPduErr TLVFieldErr = "PDU Type does not support TLV" 6 | ) 7 | 8 | type TLVField struct { 9 | Tag uint16 10 | Length uint16 11 | value []byte 12 | } 13 | 14 | type TLVFieldErr string 15 | 16 | func (t TLVFieldErr) Error() string { 17 | return string(t) 18 | } 19 | 20 | func (t *TLVField) Value() []byte { 21 | return t.value 22 | } 23 | 24 | func (t *TLVField) String() string { 25 | return string(t.Value()) 26 | } 27 | 28 | func (t *TLVField) Writer() []byte { 29 | b := []byte{} 30 | b = append(b, packUi16(t.Tag)...) 31 | b = append(b, packUi16(t.Length)...) 32 | b = append(b, t.Value()...) 33 | return b 34 | } 35 | -------------------------------------------------------------------------------- /pdu_unbind.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | var ( 4 | reqUnbindFields = []string{} 5 | ) 6 | 7 | type Unbind struct { 8 | *Header 9 | mandatoryFields map[string]Field 10 | tlvFields map[uint16]*TLVField 11 | } 12 | 13 | func NewUnbind(hdr *Header) (*Unbind, error) { 14 | s := &Unbind{Header: hdr} 15 | 16 | return s, nil 17 | } 18 | 19 | func (s *Unbind) GetField(f string) Field { 20 | return s.mandatoryFields[f] 21 | } 22 | 23 | func (s *Unbind) Fields() map[string]Field { 24 | return s.mandatoryFields 25 | } 26 | 27 | func (s *Unbind) MandatoryFieldsList() []string { 28 | return reqUnbindFields 29 | } 30 | 31 | func (s *Unbind) Ok() bool { 32 | return true 33 | } 34 | 35 | func (s *Unbind) GetHeader() *Header { 36 | return s.Header 37 | } 38 | 39 | func (s *Unbind) SetField(f string, v interface{}) error { 40 | if s.validate_field(f, v) { 41 | field := NewField(f, v) 42 | 43 | if field != nil { 44 | s.mandatoryFields[f] = field 45 | 46 | return nil 47 | } 48 | } 49 | 50 | return FieldValueErr 51 | } 52 | 53 | func (s *Unbind) SetSeqNum(i uint32) { 54 | s.Header.Sequence = i 55 | } 56 | 57 | func (s *Unbind) SetTLVField(t, l int, v []byte) error { 58 | return TLVFieldPduErr 59 | } 60 | 61 | func (s *Unbind) validate_field(f string, v interface{}) bool { 62 | if included_check(s.MandatoryFieldsList(), f) && validate_pdu_field(f, v) { 63 | return true 64 | } 65 | return false 66 | } 67 | 68 | func (s *Unbind) TLVFields() map[uint16]*TLVField { 69 | return s.tlvFields 70 | } 71 | 72 | func (s *Unbind) writeFields() []byte { 73 | b := []byte{} 74 | 75 | for _, i := range s.MandatoryFieldsList() { 76 | v := s.mandatoryFields[i].ByteArray() 77 | b = append(b, v...) 78 | } 79 | 80 | return b 81 | } 82 | 83 | func (s *Unbind) Writer() []byte { 84 | b := s.writeFields() 85 | h := packUi32(uint32(len(b) + 16)) 86 | h = append(h, packUi32(uint32(UNBIND))...) 87 | h = append(h, packUi32(uint32(s.Header.Status))...) 88 | h = append(h, packUi32(s.Header.Sequence)...) 89 | return append(h, b...) 90 | } 91 | -------------------------------------------------------------------------------- /pdu_unbind_resp.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | var ( 4 | reqUnbindRespFields = []string{} 5 | ) 6 | 7 | type UnbindResp struct { 8 | *Header 9 | mandatoryFields map[string]Field 10 | tlvFields map[uint16]*TLVField 11 | } 12 | 13 | func NewUnbindResp(hdr *Header) (*UnbindResp, error) { 14 | s := &UnbindResp{Header: hdr} 15 | 16 | return s, nil 17 | } 18 | 19 | func (s *UnbindResp) GetField(f string) Field { 20 | return nil 21 | } 22 | 23 | func (s *UnbindResp) SetField(f string, v interface{}) error { 24 | return FieldValueErr 25 | } 26 | 27 | func (s *UnbindResp) SetSeqNum(i uint32) { 28 | s.Header.Sequence = i 29 | } 30 | 31 | func (s *UnbindResp) SetTLVField(t, l int, v []byte) error { 32 | return TLVFieldPduErr 33 | } 34 | 35 | func (s *UnbindResp) Fields() map[string]Field { 36 | return s.mandatoryFields 37 | } 38 | 39 | func (s *UnbindResp) MandatoryFieldsList() []string { 40 | return reqUnbindRespFields 41 | } 42 | 43 | func (s *UnbindResp) Ok() bool { 44 | if s.Header.Status == ESME_ROK { 45 | return true 46 | } 47 | 48 | return false 49 | } 50 | 51 | func (s *UnbindResp) GetHeader() *Header { 52 | return s.Header 53 | } 54 | 55 | func (s *UnbindResp) TLVFields() map[uint16]*TLVField { 56 | return s.tlvFields 57 | } 58 | 59 | func (s *UnbindResp) writeFields() []byte { 60 | return []byte{} 61 | } 62 | 63 | func (s *UnbindResp) Writer() []byte { 64 | b := s.writeFields() 65 | h := packUi32(uint32(len(b) + 16)) 66 | h = append(h, packUi32(uint32(UNBIND_RESP))...) 67 | h = append(h, packUi32(uint32(s.Header.Status))...) 68 | h = append(h, packUi32(s.Header.Sequence)...) 69 | return append(h, b...) 70 | } 71 | -------------------------------------------------------------------------------- /receiver.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Receiver struct { 8 | Smpp 9 | eLTicker *time.Ticker // Enquire Link ticker 10 | eLCheckTimer *time.Timer // Enquire Link Check timer 11 | eLDuration int // Enquire Link Duration 12 | Err error // Errors generated in go routines that lead to conn close 13 | } 14 | 15 | // eli = EnquireLink Interval in Seconds 16 | func NewReceiver(host string, port int, eli int, bindParams Params) (*Receiver, error) { 17 | rx := &Receiver{} 18 | if err := rx.Connect(host, port); err != nil { 19 | return nil, err 20 | } 21 | 22 | sysId := bindParams[SYSTEM_ID].(string) 23 | pass := bindParams[PASSWORD].(string) 24 | 25 | if err := rx.Bind(sysId, pass, &bindParams); err != nil { 26 | return nil, err 27 | } 28 | 29 | // EnquireLinks should not be less 10seconds 30 | if eli < 10 { 31 | eli = 10 32 | } 33 | 34 | rx.eLDuration = eli 35 | 36 | go rx.startEnquireLink(eli) 37 | 38 | return rx, nil 39 | } 40 | 41 | func (t *Receiver) Bind(system_id string, password string, params *Params) error { 42 | pdu, err := t.Smpp.Bind(BIND_RECEIVER, system_id, password, params) 43 | if err := t.Write(pdu); err != nil { 44 | return err 45 | } 46 | 47 | // If BindResp NOT received in 5secs close connection 48 | go t.bindCheck() 49 | 50 | // Read (blocking) 51 | pdu, err = t.Smpp.Read() 52 | 53 | if err != nil { 54 | return err 55 | } 56 | 57 | if pdu.GetHeader().Id != BIND_RECEIVER_RESP { 58 | return SmppBindRespErr 59 | } 60 | 61 | if !pdu.Ok() { 62 | return SmppBindAuthErr("Bind auth failed. " + pdu.GetHeader().Status.Error()) 63 | } 64 | 65 | t.Bound = true 66 | 67 | return nil 68 | } 69 | 70 | func (t *Receiver) SubmitSm(source_addr, destination_addr, short_message string, params *Params) (seq uint32, err error) { 71 | return 0, SmppPduErr 72 | } 73 | 74 | func (t *Receiver) DeliverSmResp(seq uint32, status CMDStatus) error { 75 | p, err := t.Smpp.DeliverSmResp(seq, status) 76 | 77 | if err != nil { 78 | return err 79 | } 80 | 81 | if err := t.Write(p); err != nil { 82 | return err 83 | } 84 | 85 | return nil 86 | } 87 | 88 | func (t *Receiver) Unbind() error { 89 | p, _ := t.Smpp.Unbind() 90 | 91 | if err := t.Write(p); err != nil { 92 | return err 93 | } 94 | 95 | return nil 96 | } 97 | 98 | func (t *Receiver) UnbindResp(seq uint32) error { 99 | p, _ := t.Smpp.UnbindResp(seq) 100 | 101 | if err := t.Write(p); err != nil { 102 | return err 103 | } 104 | 105 | t.Bound = false 106 | 107 | return nil 108 | } 109 | 110 | func (t *Receiver) bindCheck() { 111 | // Block 112 | <-time.After(time.Duration(5 * time.Second)) 113 | if !t.Bound { 114 | // send error to t.err? So it can be read before closing. 115 | t.Err = SmppBindRespErr 116 | t.Close() 117 | } 118 | } 119 | 120 | func (t *Receiver) startEnquireLink(eli int) { 121 | t.eLTicker = time.NewTicker(time.Duration(eli) * time.Second) 122 | // check delay is half the time of enquire link intervel 123 | d := time.Duration(eli/2) * time.Second 124 | t.eLCheckTimer = time.NewTimer(d) 125 | t.eLCheckTimer.Stop() 126 | 127 | for { 128 | select { 129 | case <-t.eLTicker.C: 130 | 131 | p, _ := t.EnquireLink() 132 | if err := t.Write(p); err != nil { 133 | t.Err = SmppELWriteErr 134 | t.Close() 135 | return 136 | } 137 | 138 | t.eLCheckTimer.Reset(d) 139 | case <-t.eLCheckTimer.C: 140 | t.Err = SmppELRespErr 141 | t.Close() 142 | return 143 | } 144 | } 145 | } 146 | 147 | func (t *Receiver) Read() (Pdu, error) { 148 | pdu, err := t.Smpp.Read() 149 | if err != nil { 150 | if _, ok := err.(PduCmdIdErr); ok { 151 | // Invalid PDU Command ID, should send back GenericNack 152 | t.GenericNack(uint32(0), ESME_RINVCMDID) 153 | } else if SmppPduLenErr == err { 154 | // Invalid PDU, PDU read or Len error 155 | t.GenericNack(uint32(0), ESME_RINVCMDLEN) 156 | } 157 | 158 | return nil, err 159 | } 160 | 161 | switch pdu.GetHeader().Id { 162 | case DELIVER_SM: 163 | return pdu, nil 164 | case ENQUIRE_LINK: 165 | p, _ := t.Smpp.EnquireLinkResp(pdu.GetHeader().Sequence) 166 | 167 | if err := t.Write(p); err != nil { 168 | return nil, err 169 | } 170 | case ENQUIRE_LINK_RESP: 171 | // Reset EnquireLink Check 172 | t.eLCheckTimer.Reset(time.Duration(t.eLDuration) * time.Second) 173 | case UNBIND: 174 | t.UnbindResp(pdu.GetHeader().Sequence) 175 | t.Close() 176 | default: 177 | // Should not have received these PDUs on a RX bind 178 | return nil, SmppPduErr 179 | } 180 | 181 | return pdu, nil 182 | } 183 | 184 | func (t *Receiver) Close() { 185 | // Check timers exists incase we Close() before timers are created 186 | if t.eLCheckTimer != nil { 187 | t.eLCheckTimer.Stop() 188 | } 189 | 190 | if t.eLTicker != nil { 191 | t.eLTicker.Stop() 192 | } 193 | 194 | t.Smpp.Close() 195 | } 196 | 197 | func (t *Receiver) Write(p Pdu) error { 198 | err := t.Smpp.Write(p) 199 | 200 | return err 201 | } 202 | -------------------------------------------------------------------------------- /smpp.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "bufio" 5 | "crypto/tls" 6 | "encoding/hex" 7 | "fmt" 8 | "net" 9 | "strconv" 10 | "sync" 11 | ) 12 | 13 | var Debug bool 14 | 15 | type NewSeqNumFunc func() uint32 16 | 17 | type Smpp struct { 18 | Mu sync.Mutex 19 | conn net.Conn 20 | reader *bufio.Reader 21 | writer *bufio.Writer 22 | Sequence uint32 23 | Bound bool 24 | NewSeqNumFunc NewSeqNumFunc 25 | } 26 | 27 | type SmppErr string 28 | type SmppBindAuthErr string 29 | 30 | type Params map[string]interface{} 31 | 32 | const ( 33 | SmppBindRespErr SmppErr = "BIND Resp not received" 34 | SmppPduErr SmppErr = "PDU out of spec for this connection type" 35 | SmppPduSizeErr SmppErr = "PDU Len larger than MAX_PDU_SIZE" 36 | SmppPduLenErr SmppErr = "PDU Len different than read bytes" 37 | SmppELWriteErr SmppErr = "Error writing ELR PDU" 38 | SmppELRespErr SmppErr = "No enquire link response" 39 | ) 40 | 41 | func (p SmppErr) Error() string { 42 | return string(p) 43 | } 44 | 45 | func (p SmppBindAuthErr) Error() string { 46 | return string(p) 47 | } 48 | 49 | func NewSmppConnect(host string, port int) (*Smpp, error) { 50 | s := &Smpp{} 51 | 52 | err := s.Connect(host, port) 53 | 54 | return s, err 55 | } 56 | 57 | func (s *Smpp) Connect(host string, port int) (err error) { 58 | s.conn, err = net.Dial("tcp", host+":"+strconv.Itoa(port)) 59 | 60 | return err 61 | } 62 | 63 | func NewSmppConnectTLS(host string, port int, config *tls.Config) (*Smpp, error) { 64 | s := &Smpp{} 65 | 66 | err := s.ConnectTLS(host, port, config) 67 | 68 | return s, err 69 | } 70 | 71 | func (s *Smpp) ConnectTLS(host string, port int, config *tls.Config) error { 72 | conn, err := net.Dial("tcp", host+":"+strconv.Itoa(port)) 73 | if err != nil { 74 | return err 75 | } 76 | if config == nil { 77 | config = &tls.Config{} 78 | } 79 | s.conn = tls.Client(conn, config) 80 | return err 81 | } 82 | 83 | func (s *Smpp) NewSeqNum() uint32 { 84 | if s.NewSeqNumFunc != nil { 85 | return s.NewSeqNumFunc() 86 | } 87 | 88 | defer s.Mu.Unlock() 89 | 90 | s.Mu.Lock() 91 | s.Sequence++ 92 | return s.Sequence 93 | } 94 | 95 | func (s *Smpp) Bind(cmdId CMDId, system_id string, password string, params *Params) (Pdu, error) { 96 | b, _ := NewBind( 97 | &Header{Id: cmdId}, 98 | []byte{}, 99 | ) 100 | 101 | b.SetField(INTERFACE_VERSION, 0x34) 102 | b.SetField(SYSTEM_ID, system_id) 103 | b.SetField(PASSWORD, password) 104 | b.SetSeqNum(s.NewSeqNum()) 105 | 106 | for f, v := range *params { 107 | err := b.SetField(f, v) 108 | 109 | if err != nil { 110 | return nil, err 111 | } 112 | } 113 | 114 | return Pdu(b), nil 115 | } 116 | 117 | func (s *Smpp) BindResp(cmdId CMDId, seq uint32, status CMDStatus, sysId string) (Pdu, error) { 118 | p, _ := NewBindResp( 119 | &Header{ 120 | Id: cmdId, 121 | Status: status, 122 | Sequence: seq, 123 | }, 124 | []byte{}, 125 | ) 126 | 127 | p.SetField(SYSTEM_ID, sysId) 128 | p.SetTLVField(0x0210, 1, []byte{0x34}) // sc_interface_version TLV 129 | 130 | return Pdu(p), nil 131 | } 132 | 133 | func (s *Smpp) EnquireLink() (Pdu, error) { 134 | p, _ := NewEnquireLink( 135 | &Header{ 136 | Id: ENQUIRE_LINK, 137 | Sequence: s.NewSeqNum(), 138 | }, 139 | ) 140 | 141 | return Pdu(p), nil 142 | } 143 | 144 | func (s *Smpp) EnquireLinkResp(seq uint32) (Pdu, error) { 145 | p, _ := NewEnquireLinkResp( 146 | &Header{ 147 | Id: ENQUIRE_LINK_RESP, 148 | Status: ESME_ROK, 149 | Sequence: seq, 150 | }, 151 | ) 152 | 153 | return Pdu(p), nil 154 | } 155 | 156 | func (s *Smpp) SubmitSm(source_addr, destination_addr, short_message string, params *Params) (Pdu, error) { 157 | 158 | p, _ := NewSubmitSm( 159 | &Header{ 160 | Id: SUBMIT_SM, 161 | Sequence: s.NewSeqNum(), 162 | }, 163 | []byte{}, 164 | ) 165 | 166 | p.SetField(SOURCE_ADDR, source_addr) 167 | p.SetField(DESTINATION_ADDR, destination_addr) 168 | p.SetField(SHORT_MESSAGE, short_message) 169 | 170 | for f, v := range *params { 171 | err := p.SetField(f, v) 172 | 173 | if err != nil { 174 | return nil, err 175 | } 176 | } 177 | 178 | return p, nil 179 | } 180 | 181 | func (s *Smpp) SubmitSmEncoded(source_addr, destination_addr string, short_message []byte, params *Params) (Pdu, error) { 182 | 183 | p, _ := NewSubmitSm( 184 | &Header{ 185 | Id: SUBMIT_SM, 186 | Sequence: s.NewSeqNum(), 187 | }, 188 | []byte{}, 189 | ) 190 | 191 | p.SetField(SOURCE_ADDR, source_addr) 192 | p.SetField(DESTINATION_ADDR, destination_addr) 193 | p.SetField(SHORT_MESSAGE, short_message) 194 | 195 | for f, v := range *params { 196 | err := p.SetField(f, v) 197 | 198 | if err != nil { 199 | return nil, err 200 | } 201 | } 202 | 203 | return p, nil 204 | } 205 | 206 | func (s *Smpp) SubmitSmResp(seq uint32, status CMDStatus, messageId string) (Pdu, error) { 207 | p, _ := NewSubmitSmResp( 208 | &Header{ 209 | Id: SUBMIT_SM_RESP, 210 | Status: status, 211 | Sequence: seq, 212 | }, 213 | []byte{}, 214 | ) 215 | 216 | p.SetField(MESSAGE_ID, messageId) 217 | 218 | return Pdu(p), nil 219 | } 220 | 221 | func (s *Smpp) QuerySm(message_id, source_addr string, params *Params) (Pdu, error) { 222 | 223 | p, _ := NewQuerySm( 224 | &Header{ 225 | Id: QUERY_SM, 226 | Sequence: s.NewSeqNum(), 227 | }, 228 | []byte{}, 229 | ) 230 | 231 | p.SetField(MESSAGE_ID, message_id) 232 | p.SetField(SOURCE_ADDR, source_addr) 233 | 234 | for f, v := range *params { 235 | err := p.SetField(f, v) 236 | 237 | if err != nil { 238 | return nil, err 239 | } 240 | } 241 | 242 | return p, nil 243 | } 244 | 245 | func (s *Smpp) Unbind() (Pdu, error) { 246 | p, _ := NewUnbind( 247 | &Header{ 248 | Id: UNBIND, 249 | Sequence: s.NewSeqNum(), 250 | }, 251 | ) 252 | 253 | return Pdu(p), nil 254 | } 255 | 256 | func (s *Smpp) UnbindResp(seq uint32) (Pdu, error) { 257 | p, _ := NewUnbindResp( 258 | &Header{ 259 | Id: UNBIND_RESP, 260 | Sequence: seq, 261 | }, 262 | ) 263 | 264 | return Pdu(p), nil 265 | } 266 | 267 | func (s *Smpp) DeliverSmResp(seq uint32, status CMDStatus) (Pdu, error) { 268 | p, _ := NewDeliverSmResp( 269 | &Header{ 270 | Id: DELIVER_SM_RESP, 271 | Status: status, 272 | Sequence: seq, 273 | }, 274 | []byte{}, 275 | ) 276 | 277 | return Pdu(p), nil 278 | } 279 | 280 | func (s *Smpp) GenericNack(seq uint32, status CMDStatus) (Pdu, error) { 281 | p, _ := NewGenericNack( 282 | &Header{ 283 | Id: GENERIC_NACK, 284 | Status: status, 285 | Sequence: seq, 286 | }, 287 | ) 288 | 289 | return Pdu(p), nil 290 | } 291 | 292 | func (s *Smpp) Read() (Pdu, error) { return SmppReadFrom(s.conn) } 293 | 294 | func (s *Smpp) Write(p Pdu) error { 295 | _, err := s.conn.Write(p.Writer()) 296 | 297 | if Debug { 298 | fmt.Println(hex.Dump(p.Writer())) 299 | } 300 | 301 | return err 302 | } 303 | 304 | func (s *Smpp) Close() { 305 | s.conn.Close() 306 | } 307 | 308 | func SmppReadFrom(conn net.Conn) (Pdu, error) { 309 | l := make([]byte, 4) 310 | _, err := conn.Read(l) 311 | if err != nil { 312 | return nil, err 313 | } 314 | 315 | pduLength := unpackUi32(l) - 4 316 | if pduLength > MAX_PDU_SIZE { 317 | return nil, SmppPduSizeErr 318 | } 319 | 320 | data := make([]byte, pduLength) 321 | 322 | i, err := conn.Read(data) 323 | if err != nil { 324 | return nil, err 325 | } 326 | 327 | if i != int(pduLength) { 328 | return nil, SmppPduLenErr 329 | } 330 | 331 | pkt := append(l, data...) 332 | if Debug { 333 | fmt.Println(hex.Dump(pkt)) 334 | } 335 | 336 | pdu, err := ParsePdu(pkt) 337 | if err != nil { 338 | return nil, err 339 | } 340 | 341 | return pdu, nil 342 | } 343 | -------------------------------------------------------------------------------- /smpp_test.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/hex" 6 | "net" 7 | "strconv" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestClient(t *testing.T) { 13 | testClient(t, nil) 14 | } 15 | 16 | func TestClientTLS(t *testing.T) { 17 | cert, err := tls.X509KeyPair(localhostCert, localhostKey) 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | tc := tls.Config{ 22 | Certificates: []tls.Certificate{cert}, 23 | } 24 | testClient(t, &tc) 25 | } 26 | 27 | func testClient(t *testing.T, config *tls.Config) { 28 | s, err := testServer(config) 29 | if err != nil { 30 | t.Fatal("Failed to parse host and port:", err) 31 | } 32 | defer s.Close() 33 | host, port, err := s.Addr() 34 | if err != nil { 35 | t.Fatal("Failed parsing server addr:", err) 36 | } 37 | 38 | var c *Smpp 39 | if config == nil { 40 | c, err = NewSmppConnect(host, port) 41 | } else { 42 | config.InsecureSkipVerify = true 43 | c, err = NewSmppConnectTLS(host, port, config) 44 | } 45 | if err != nil { 46 | t.Fatal("Failed to connect:", err) 47 | } 48 | 49 | p, err := c.Bind(BIND_TRANSCEIVER, "test", "pass", &Params{}) 50 | if err != nil { 51 | t.Fatal("Failed to bind:", err) 52 | } 53 | c.Write(p) 54 | 55 | p, err = c.Read() 56 | if err != nil { 57 | t.Fatal("Failed to read bind response:", err) 58 | } 59 | 60 | if v := p.GetHeader().Id; v != BIND_TRANSCEIVER_RESP { 61 | t.Fatalf("Unexpected Id: want %q, have %q", 62 | v, BIND_TRANSCEIVER_RESP) 63 | } 64 | 65 | if v := p.GetField(SYSTEM_ID).String(); v != "hugo" { 66 | t.Fatalf("Unexpected SYSTEM_ID: want \"hugo\", have %q", v) 67 | } 68 | _, err = c.Read() 69 | if err != nil && err.Error() != "Unknown PDU Type. ID:2415919125" { 70 | t.Fatal("Unexpected error:", err) 71 | } 72 | _, err = c.Read() 73 | if err != nil && err.Error() != "PDU Len different than read bytes" { 74 | t.Fatal("Unexpected error:", err) 75 | } 76 | _, err = c.Read() 77 | if err != nil && err.Error() != "PDU Len larger than MAX_PDU_SIZE" { 78 | t.Fatal("Unexpected error:", err) 79 | } 80 | } 81 | 82 | type server struct { 83 | l net.Listener 84 | } 85 | 86 | func testServer(config *tls.Config) (*server, error) { 87 | l, err := net.Listen("tcp", "127.0.0.1:0") 88 | if err != nil { 89 | return nil, err 90 | } 91 | if config != nil { 92 | l = tls.NewListener(l, config) 93 | } 94 | go func(l net.Listener) { 95 | for { 96 | conn, err := l.Accept() 97 | if err != nil { 98 | return // expected by Close 99 | } 100 | go handleConnection(conn) 101 | } 102 | }(l) 103 | return &server{l}, nil 104 | } 105 | 106 | func (s *server) Addr() (host string, port int, err error) { 107 | h, p, err := net.SplitHostPort(s.l.Addr().String()) 108 | if err != nil { 109 | return "", 0, err 110 | } 111 | pn, err := strconv.Atoi(p) 112 | if err != nil { 113 | return "", 0, err 114 | } 115 | return h, pn, nil 116 | } 117 | 118 | func (s *server) Close() error { 119 | return s.l.Close() 120 | } 121 | 122 | func handleConnection(n net.Conn) { 123 | l := make([]byte, 1024) 124 | _, err := n.Read(l) 125 | if err != nil { 126 | panic("server read error") 127 | } 128 | 129 | // Bind Resp 130 | d, _ := hex.DecodeString("000000158000000900000000000000016875676f00") 131 | n.Write(d) 132 | 133 | time.Sleep(100 * time.Millisecond) 134 | 135 | // Invalid CMD ID 136 | d, _ = hex.DecodeString("0000001090000015000000005222b523") 137 | n.Write(d) 138 | 139 | time.Sleep(100 * time.Millisecond) 140 | 141 | // PDU Len different than read bytes 142 | d, _ = hex.DecodeString("000000178000000900000000000000016875676f00") 143 | n.Write(d) 144 | 145 | time.Sleep(100 * time.Millisecond) 146 | 147 | // Max PDU Len err 148 | d, _ = hex.DecodeString("0000F01080000015000000005222b526") 149 | n.Write(d) 150 | } 151 | 152 | // localhostCert is a PEM-encoded TLS cert with SAN IPs 153 | // "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end 154 | // of ASN.1 time). 155 | // generated from src/crypto/tls: 156 | // go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h 157 | var localhostCert = []byte(`-----BEGIN CERTIFICATE----- 158 | MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS 159 | MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw 160 | MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB 161 | iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4 162 | iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul 163 | rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO 164 | BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw 165 | AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA 166 | AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9 167 | tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs 168 | h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM 169 | fblo6RBxUQ== 170 | -----END CERTIFICATE-----`) 171 | 172 | // localhostKey is the private key for localhostCert. 173 | var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- 174 | MIICXgIBAAKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9 175 | SjY1bIw4iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZB 176 | l2+XsDulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB 177 | AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet 178 | 3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb 179 | uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H 180 | qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp 181 | jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY 182 | fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U 183 | fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU 184 | y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj013sovGKUFfYAqVXVlxtIX 185 | qyUBnu3X9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFeo 186 | f9Oeos0UUothgiDktdQHxdNEwLjQf7lJJBzV+5OtwswCWA== 187 | -----END RSA PRIVATE KEY-----`) 188 | -------------------------------------------------------------------------------- /transceiver.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "crypto/tls" 5 | "time" 6 | ) 7 | 8 | type Transceiver struct { 9 | Smpp 10 | eLTicker *time.Ticker // Enquire Link ticker 11 | eLCheckTimer *time.Timer // Enquire Link Check timer 12 | eLDuration int // Enquire Link Duration 13 | Err error // Errors generated in go routines that lead to conn close 14 | } 15 | 16 | func (t *Transceiver) ELDuration(v int) { t.eLDuration = v } 17 | 18 | // NewTransceiver creates and initializes a new Transceiver. 19 | // The eli parameter is for EnquireLink interval, in seconds. 20 | func NewTransceiver(host string, port int, eli int, bindParams Params) (*Transceiver, error) { 21 | return newTransceiver(host, port, eli, bindParams, nil) 22 | } 23 | 24 | // NewTransceiver creates and initializes a new Transceiver using TLS. 25 | // The eli parameter is for EnquireLink interval, in seconds. 26 | func NewTransceiverTLS(host string, port int, eli int, bindParams Params, config *tls.Config) (*Transceiver, error) { 27 | if config == nil { 28 | config = &tls.Config{} 29 | } 30 | return newTransceiver(host, port, eli, bindParams, config) 31 | } 32 | 33 | // eli = EnquireLink Interval in Seconds 34 | func newTransceiver(host string, port int, eli int, bindParams Params, config *tls.Config) (*Transceiver, error) { 35 | trx := &Transceiver{} 36 | var err error 37 | if config == nil { 38 | err = trx.Connect(host, port) 39 | } else { 40 | err = trx.ConnectTLS(host, port, config) 41 | } 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | sysId := bindParams[SYSTEM_ID].(string) 47 | pass := bindParams[PASSWORD].(string) 48 | 49 | if err := trx.Bind(sysId, pass, &bindParams); err != nil { 50 | return nil, err 51 | } 52 | 53 | // EnquireLinks should not be less 10seconds 54 | if eli < 10 { 55 | eli = 10 56 | } 57 | 58 | trx.eLDuration = eli 59 | 60 | go trx.startEnquireLink(eli) 61 | 62 | return trx, nil 63 | } 64 | 65 | func (t *Transceiver) Bind(system_id string, password string, params *Params) error { 66 | pdu, err := t.Smpp.Bind(BIND_TRANSCEIVER, system_id, password, params) 67 | if err := t.Write(pdu); err != nil { 68 | return err 69 | } 70 | 71 | // If BindResp NOT received in 5secs close connection 72 | go t.bindCheck() 73 | 74 | // Read (blocking) 75 | pdu, err = t.Smpp.Read() 76 | 77 | if err != nil { 78 | return err 79 | } 80 | 81 | if pdu.GetHeader().Id != BIND_TRANSCEIVER_RESP { 82 | return SmppBindRespErr 83 | } 84 | 85 | if !pdu.Ok() { 86 | return SmppBindAuthErr("Bind auth failed. " + pdu.GetHeader().Status.Error()) 87 | } 88 | 89 | t.Bound = true 90 | 91 | return nil 92 | } 93 | 94 | func (t *Transceiver) SubmitSm(source_addr, destination_addr, short_message string, params *Params) (seq uint32, err error) { 95 | p, err := t.Smpp.SubmitSm(source_addr, destination_addr, short_message, params) 96 | 97 | if err != nil { 98 | return 0, err 99 | } 100 | 101 | if err := t.Write(p); err != nil { 102 | return 0, err 103 | } 104 | 105 | return p.GetHeader().Sequence, nil 106 | } 107 | 108 | func (t *Transceiver) SubmitSmEncoded(source_addr, destination_addr string, short_message []byte, params *Params) (seq uint32, err error) { 109 | p, err := t.Smpp.SubmitSmEncoded(source_addr, destination_addr, short_message, params) 110 | if err != nil { 111 | return 0, err 112 | } 113 | 114 | if err := t.Write(p); err != nil { 115 | return 0, err 116 | } 117 | 118 | return p.GetHeader().Sequence, nil 119 | } 120 | 121 | func (t *Transceiver) DeliverSmResp(seq uint32, status CMDStatus) error { 122 | p, err := t.Smpp.DeliverSmResp(seq, status) 123 | 124 | if err != nil { 125 | return err 126 | } 127 | 128 | if err := t.Write(p); err != nil { 129 | return err 130 | } 131 | 132 | return nil 133 | } 134 | 135 | func (t *Transceiver) Unbind() error { 136 | p, _ := t.Smpp.Unbind() 137 | 138 | if err := t.Write(p); err != nil { 139 | return err 140 | } 141 | 142 | return nil 143 | } 144 | 145 | func (t *Transceiver) UnbindResp(seq uint32) error { 146 | p, _ := t.Smpp.UnbindResp(seq) 147 | 148 | if err := t.Write(p); err != nil { 149 | return err 150 | } 151 | 152 | t.Bound = false 153 | 154 | return nil 155 | } 156 | 157 | func (t *Transceiver) GenericNack(seq uint32, status CMDStatus) error { 158 | p, _ := t.Smpp.GenericNack(seq, status) 159 | 160 | if err := t.Write(p); err != nil { 161 | return err 162 | } 163 | 164 | return nil 165 | } 166 | 167 | func (t *Transceiver) bindCheck() { 168 | // Block 169 | <-time.After(time.Duration(5 * time.Second)) 170 | if !t.Bound { 171 | // send error to t.err? So it can be read before closing. 172 | t.Err = SmppBindRespErr 173 | t.Close() 174 | } 175 | } 176 | 177 | func (t *Transceiver) StartEnquireLink(eli int) { t.startEnquireLink(eli) } 178 | func (t *Transceiver) startEnquireLink(eli int) { 179 | t.eLTicker = time.NewTicker(time.Duration(eli) * time.Second) 180 | // check delay is half the time of enquire link intervel 181 | d := time.Duration(eli/2) * time.Second 182 | t.eLCheckTimer = time.NewTimer(d) 183 | t.eLCheckTimer.Stop() 184 | 185 | for { 186 | select { 187 | case <-t.eLTicker.C: 188 | 189 | p, _ := t.EnquireLink() 190 | if err := t.Write(p); err != nil { 191 | t.Err = SmppELWriteErr 192 | t.Close() 193 | return 194 | } 195 | 196 | t.eLCheckTimer.Reset(d) 197 | case <-t.eLCheckTimer.C: 198 | t.Err = SmppELRespErr 199 | t.Close() 200 | return 201 | } 202 | } 203 | } 204 | 205 | func (t *Transceiver) Read() (Pdu, error) { 206 | pdu, err := t.Smpp.Read() 207 | if err != nil { 208 | if _, ok := err.(PduCmdIdErr); ok { 209 | // Invalid PDU Command ID, should send back GenericNack 210 | t.GenericNack(uint32(0), ESME_RINVCMDID) 211 | } else if SmppPduLenErr == err { 212 | // Invalid PDU, PDU read or Len error 213 | t.GenericNack(uint32(0), ESME_RINVCMDLEN) 214 | } 215 | 216 | return nil, err 217 | } 218 | 219 | switch pdu.GetHeader().Id { 220 | case SUBMIT_SM_RESP, DELIVER_SM, QUERY_SM_RESP: 221 | return pdu, nil 222 | case ENQUIRE_LINK: 223 | p, _ := t.Smpp.EnquireLinkResp(pdu.GetHeader().Sequence) 224 | 225 | if err := t.Write(p); err != nil { 226 | return nil, err 227 | } 228 | case ENQUIRE_LINK_RESP: 229 | // Reset EnquireLink Check 230 | t.eLCheckTimer.Reset(time.Duration(t.eLDuration) * time.Second) 231 | case UNBIND: 232 | t.UnbindResp(pdu.GetHeader().Sequence) 233 | t.Close() 234 | default: 235 | // Should not have received these PDUs on a TRx bind 236 | return nil, SmppPduErr 237 | } 238 | 239 | return pdu, nil 240 | } 241 | 242 | func (t *Transceiver) Close() { 243 | // Check timers exists incase we Close() before timers are created 244 | if t.eLCheckTimer != nil { 245 | t.eLCheckTimer.Stop() 246 | } 247 | 248 | if t.eLTicker != nil { 249 | t.eLTicker.Stop() 250 | } 251 | 252 | t.Smpp.Close() 253 | } 254 | 255 | func (t *Transceiver) Write(p Pdu) error { 256 | err := t.Smpp.Write(p) 257 | 258 | return err 259 | } 260 | -------------------------------------------------------------------------------- /transmitter.go: -------------------------------------------------------------------------------- 1 | package smpp34 2 | 3 | import ( 4 | "crypto/tls" 5 | "time" 6 | ) 7 | 8 | type Transmitter struct { 9 | Smpp 10 | eLTicker *time.Ticker // Enquire Link ticker 11 | eLCheckTimer *time.Timer // Enquire Link Check timer 12 | eLDuration int // Enquire Link Duration 13 | Err error // Errors generated in go routines that lead to conn close 14 | } 15 | 16 | // NewTransmitter creates and initializes a new Transmitter. 17 | // The eli parameter is for EnquireLink interval, in seconds. 18 | func NewTransmitter(host string, port int, eli int, bindParams Params) (*Transmitter, error) { 19 | return newTransmitter(host, port, eli, bindParams, nil) 20 | } 21 | 22 | // NewTransmitterTLs creates and initializes a new Transmitter using TLS. 23 | // The eli parameter is for EnquireLink interval, in seconds. 24 | func NewTransmitterTLS(host string, port int, eli int, bindParams Params, config *tls.Config) (*Transmitter, error) { 25 | if config == nil { 26 | config = &tls.Config{} 27 | } 28 | return newTransmitter(host, port, eli, bindParams, config) 29 | } 30 | 31 | // eli = EnquireLink Interval in Seconds 32 | func newTransmitter(host string, port int, eli int, bindParams Params, config *tls.Config) (*Transmitter, error) { 33 | tx := &Transmitter{} 34 | var err error 35 | if config == nil { 36 | err = tx.Connect(host, port) 37 | } else { 38 | err = tx.ConnectTLS(host, port, config) 39 | } 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | sysId := bindParams[SYSTEM_ID].(string) 45 | pass := bindParams[PASSWORD].(string) 46 | 47 | if err := tx.Bind(sysId, pass, &bindParams); err != nil { 48 | return nil, err 49 | } 50 | 51 | // EnquireLinks should not be less 10seconds 52 | if eli < 10 { 53 | eli = 10 54 | } 55 | 56 | tx.eLDuration = eli 57 | 58 | go tx.startEnquireLink(eli) 59 | 60 | return tx, nil 61 | } 62 | 63 | func (t *Transmitter) Bind(system_id string, password string, params *Params) error { 64 | pdu, err := t.Smpp.Bind(BIND_TRANSMITTER, system_id, password, params) 65 | if err := t.Write(pdu); err != nil { 66 | return err 67 | } 68 | 69 | // If BindResp NOT received in 5secs close connection 70 | go t.bindCheck() 71 | 72 | // Read (blocking) 73 | pdu, err = t.Smpp.Read() 74 | 75 | if err != nil { 76 | return err 77 | } 78 | 79 | if pdu.GetHeader().Id != BIND_TRANSMITTER_RESP { 80 | return SmppBindRespErr 81 | } 82 | 83 | if !pdu.Ok() { 84 | return SmppBindAuthErr("Bind auth failed. " + pdu.GetHeader().Status.Error()) 85 | } 86 | 87 | t.Bound = true 88 | 89 | return nil 90 | } 91 | 92 | func (t *Transmitter) SubmitSm(source_addr, destination_addr, short_message string, params *Params) (seq uint32, err error) { 93 | p, err := t.Smpp.SubmitSm(source_addr, destination_addr, short_message, params) 94 | 95 | if err != nil { 96 | return 0, err 97 | } 98 | 99 | if err := t.Write(p); err != nil { 100 | return 0, err 101 | } 102 | 103 | return p.GetHeader().Sequence, nil 104 | } 105 | 106 | func (t *Transmitter) SubmitSmEncoded(source_addr, destination_addr string, short_message []byte, params *Params) (seq uint32, err error) { 107 | p, err := t.Smpp.SubmitSmEncoded(source_addr, destination_addr, short_message, params) 108 | 109 | if err != nil { 110 | return 0, err 111 | } 112 | 113 | if err := t.Write(p); err != nil { 114 | return 0, err 115 | } 116 | 117 | return p.GetHeader().Sequence, nil 118 | } 119 | 120 | func (t *Transmitter) QuerySm(message_id, source_addr string, params *Params) (seq uint32, err error) { 121 | p, err := t.Smpp.QuerySm(message_id, source_addr, params) 122 | 123 | if err != nil { 124 | return 0, err 125 | } 126 | 127 | if err := t.Write(p); err != nil { 128 | return 0, err 129 | } 130 | 131 | return p.GetHeader().Sequence, nil 132 | } 133 | 134 | func (t *Transmitter) DeliverSmResp(seq, status uint32) error { 135 | return SmppPduErr 136 | } 137 | 138 | func (t *Transmitter) Unbind() error { 139 | p, _ := t.Smpp.Unbind() 140 | 141 | if err := t.Write(p); err != nil { 142 | return err 143 | } 144 | 145 | return nil 146 | } 147 | 148 | func (t *Transmitter) UnbindResp(seq uint32) error { 149 | p, _ := t.Smpp.UnbindResp(seq) 150 | 151 | if err := t.Write(p); err != nil { 152 | return err 153 | } 154 | 155 | t.Bound = false 156 | 157 | return nil 158 | } 159 | 160 | func (t *Transmitter) bindCheck() { 161 | // Block 162 | <-time.After(time.Duration(5 * time.Second)) 163 | if !t.Bound { 164 | // send error to t.err? So it can be read before closing. 165 | t.Err = SmppBindRespErr 166 | t.Close() 167 | } 168 | } 169 | 170 | func (t *Transmitter) startEnquireLink(eli int) { 171 | t.eLTicker = time.NewTicker(time.Duration(eli) * time.Second) 172 | // check delay is half the time of enquire link intervel 173 | d := time.Duration(eli/2) * time.Second 174 | t.eLCheckTimer = time.NewTimer(d) 175 | t.eLCheckTimer.Stop() 176 | 177 | for { 178 | select { 179 | case <-t.eLTicker.C: 180 | 181 | p, _ := t.EnquireLink() 182 | if err := t.Write(p); err != nil { 183 | t.Err = SmppELWriteErr 184 | t.Close() 185 | return 186 | } 187 | 188 | t.eLCheckTimer.Reset(d) 189 | case <-t.eLCheckTimer.C: 190 | t.Err = SmppELRespErr 191 | t.Close() 192 | return 193 | } 194 | } 195 | } 196 | 197 | func (t *Transmitter) Read() (Pdu, error) { 198 | pdu, err := t.Smpp.Read() 199 | if err != nil { 200 | if _, ok := err.(PduCmdIdErr); ok { 201 | // Invalid PDU Command ID, should send back GenericNack 202 | t.GenericNack(uint32(0), ESME_RINVCMDID) 203 | } else if SmppPduLenErr == err { 204 | // Invalid PDU, PDU read or Len error 205 | t.GenericNack(uint32(0), ESME_RINVCMDLEN) 206 | } 207 | 208 | return nil, err 209 | } 210 | 211 | switch pdu.GetHeader().Id { 212 | case SUBMIT_SM_RESP: 213 | return pdu, nil 214 | case QUERY_SM_RESP: 215 | return pdu, nil 216 | case ENQUIRE_LINK: 217 | p, _ := t.Smpp.EnquireLinkResp(pdu.GetHeader().Sequence) 218 | 219 | if err := t.Write(p); err != nil { 220 | return nil, err 221 | } 222 | case ENQUIRE_LINK_RESP: 223 | // Stop EnquireLink Check. 224 | t.eLCheckTimer.Stop() 225 | case UNBIND: 226 | t.UnbindResp(pdu.GetHeader().Sequence) 227 | t.Close() 228 | default: 229 | // Should not have received these PDUs on a TX bind 230 | return nil, SmppPduErr 231 | } 232 | 233 | return pdu, nil 234 | } 235 | 236 | func (t *Transmitter) Close() { 237 | // Check timers exists incase we Close() before timers are created 238 | if t.eLCheckTimer != nil { 239 | t.eLCheckTimer.Stop() 240 | } 241 | 242 | if t.eLTicker != nil { 243 | t.eLTicker.Stop() 244 | } 245 | 246 | t.Smpp.Close() 247 | } 248 | 249 | func (t *Transmitter) Write(p Pdu) error { 250 | err := t.Smpp.Write(p) 251 | 252 | return err 253 | } 254 | --------------------------------------------------------------------------------