苹果推送测试程序

#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <netdb.h>  
#include <arpa/inet.h>  
#include <string.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <errno.h>    
#include <stdlib.h>  
#include <stdint.h>  
#include <assert.h>  
  
#include <openssl/ssl.h>  
#include <openssl/bio.h>  
#include <openssl/err.h>  
  
  
#define MAX_PAYLOAD_SIZE 256  
#define TOKEN_SIZE 32  
  
void DeviceToken2Binary(const char* sz, const int len, unsigned char* const binary, const int size)   
{  
    int         i, val;  
    const char*     pin;  
    char        buf[3] = {0};  
  
    assert(size >= TOKEN_SIZE);  
  
    for (i = 0;i < len;i++)  
    {  
        pin = sz + i * 2;  
        buf[0] = pin[0];  
        buf[1] = pin[1];  
  
        val = 0;  
        sscanf(buf, "%X", &val);  
        binary[i] = val;  
    }  
  
    return;  
}  
  
void DeviceBinary2Token(const unsigned char* data, const int len, char* const token, const int size)   
{  
    int i;  
  
    assert(size > TOKEN_SIZE * 2);  
  
    for (i = 0;i < len;i++)  
    {  
        sprintf(token + i * 2, "%02x", data[i]);  
    }  
  
    return;  
}  
  
  
// 初始化ssl库
void init_openssl()  
{  
    SSL_library_init();  
    ERR_load_BIO_strings();  
    SSL_load_error_strings();  
    OpenSSL_add_all_algorithms();  
}  
  
SSL_CTX* init_ssl_context(  
        const char* clientcert, /* 客户端的证书 */  
        const char* clientkey, /* 客户端的Key */  
        const char* keypwd, /* 客户端Key的密码, 如果有的话 */  
        const char* cacert) /* 服务器CA证书 如果有的话 */  
{  
    // set up the ssl context  
    SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());  
    if (!ctx) 
{  
        return NULL;  
    }  
  
    // certificate  
    if (SSL_CTX_use_certificate_file(ctx, clientcert, SSL_FILETYPE_PEM) <= 0) 
{  
        return NULL;  
    }  
  
    // key  
    if (SSL_CTX_use_PrivateKey_file(ctx, clientkey, SSL_FILETYPE_PEM) <= 0) 
{  
        return NULL;  
    }  
  
    // make sure the key and certificate file match  
    if (SSL_CTX_check_private_key(ctx) == 0) 
{  
        return NULL;  
    }  
  
    // load ca if exist  
    if (cacert) 
{  
        if (!SSL_CTX_load_verify_locations(ctx, cacert, NULL)) 
{  
            return NULL;  
        }  
    }  
  
    return ctx;  
}  
  
// 建立TCP连接到服务器  
int tcp_connect(const char* host, int port)  
{  
    struct hostent *hp;  
    struct sockaddr_in addr;  
    int sock = -1;  
  
    // 解析域名  
    if (!(hp = gethostbyname(host))) 
{  
        return -1;  
    }  
  
    memset(&addr, 0, sizeof(addr));  
    addr.sin_addr = *(struct in_addr*)hp->h_addr_list[0];  
    addr.sin_family = AF_INET;  
    addr.sin_port = htons(port);  
  
    if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{  
        return -1;  
    }  
  
    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) 
{  
        return -1;  
    }  
  
    return sock;  
}  
  
// 实现SSL握手,建立SSL连接  
SSL* ssl_connect(SSL_CTX* ctx, int socket)  
{  
    SSL *ssl = SSL_new(ctx);  
    BIO *bio = BIO_new_socket(socket, BIO_NOCLOSE);  
    SSL_set_bio(ssl, bio, bio);  
  
    if (SSL_connect(ssl) <= 0)
{  
        return NULL;  
    }  
  
    return ssl;  
}  
  
// 验证服务器证书  
// 首先要验证服务器的证书有效,其次要验证服务器证书的CommonName(CN)与我们  
// 实际要连接的服务器域名一致  
int verify_connection(SSL* ssl, const char* peername)  
{  
    int result = SSL_get_verify_result(ssl);  
    if (result != X509_V_OK)
{  
        fprintf(stderr, "WARNING! ssl verify failed: %d", result);  
        return -1;  
    }  
  
    X509 *peer;  
    char peer_CN[256] = {0};  
  
    peer = SSL_get_peer_certificate(ssl);  
    X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, peer_CN, 255);  
    if (strcmp(peer_CN, peername) != 0) 
{  
        fprintf(stderr, "WARNING! Server Name Doesn‘t match, got: %s, required: %s", peer_CN, peername);  
    }  

    return 0;  
}  
  
void json_escape(char*  str)  
{  
    int     n;  
    char    buf[1024];  
  
    n = strlen(str) * sizeof(char) + 100;  
    assert(n < sizeof(buf));  
  
    strncpy(buf, str, n);  
    buf[n] = ‘\0‘;  
    char *found = buf;  
    while (*found != ‘\0‘)  
    {  
        if(‘\\‘ == *found || ‘"‘ == *found || ‘\n‘ == *found || ‘/‘ == *found)  
            *str++ = ‘\\‘;  
  
        if(‘\n‘ == *found)  
            *found = ‘n‘;  
  
        *str++ = *found++;         
    }  
  
    *str=‘\0‘;  
  
    return;  
}  
  
// Payload example  
  
// {"aps":{"alert" : "You got your emails.","badge" : 9,"sound" : "default"}}  
int build_payload(char* buffer, int* plen, char* msg, int badage, const char * sound)  
{  
    int n;  
    char buf[2048];  
    char str[2048] = "{\"aps\":{\"alert\":\"";  
  
    n = strlen(str);  
  
    if (msg)   
    {  
        strcpy(buf, msg);  
        json_escape(buf);  
        n = n + sprintf(str+n, "%s", buf);  
    }  
  
    n = n + sprintf(str+n, "%s%d", "\",\"badge\":", badage);  
  
    if (sound)   
    {  
        n = n + sprintf(str+n, "%s", ",\"sound\":\"");  
        strcpy(buf, sound);  
        json_escape(buf);  
        n = n + sprintf(str+n, "%s%s", buf, "\"");  
    }  
  
    strcat(str, "}}");  
  
    n = strlen(str);  
  
    if (n > *plen)   
    {  
        *plen = n;  
        return -1;  
    }  
  
  
    if (n < *plen)   
    {  
        strcpy(buffer, str);  
    }
else   
    {  
        strncpy(buffer, str, *plen);  
    }  


    *plen = n;  
  
    return *plen;  
}  
  
// 第一种形式的包  
int build_output_packet(char* buf, int buflen, /* 输出的缓冲区及长度 */  
        const char* tokenbinary, /* 二进制的Token */  
        char* msg, /* 要发送的消息 */  
        int badage, /* 应用图标上显示的数字 */  
        const char * sound) /* 设备收到时播放的声音,可以为空 */  
{  
    assert(buflen >= 1 + 2 + TOKEN_SIZE + 2 + MAX_PAYLOAD_SIZE);  
  
    char * pdata = buf;  
    // command  
    *pdata = 0;  
  
    // token length  
    pdata++;  
    *(uint16_t*)pdata = htons(TOKEN_SIZE);  
  
    // token binary  
    pdata += 2;  
    memcpy(pdata, tokenbinary, TOKEN_SIZE);  
  
    pdata += TOKEN_SIZE;  
  
    int payloadlen = MAX_PAYLOAD_SIZE;  
    if (build_payload(pdata + 2, &payloadlen, msg, badage, sound) < 0)   
    {  
        msg[strlen(msg) - (payloadlen - MAX_PAYLOAD_SIZE)] = ‘\0‘;  
        payloadlen = MAX_PAYLOAD_SIZE;  
        if (build_payload(pdata + 2, &payloadlen, msg, badage, sound) <= 0)   
        {  
            return -1;  
        }  
    }  
    *(uint16_t*)pdata = htons(payloadlen);  
  
    return 1 + 2 + TOKEN_SIZE + 2 + payloadlen;  
}  
  
int send_message(SSL *ssl, const char* token, char* msg, int badage, const char* sound)  
{  
    int         n;  
    char        buf[1 + 2 + TOKEN_SIZE + 2 + MAX_PAYLOAD_SIZE];  
    unsigned char   binary[TOKEN_SIZE];  
    int         buflen = sizeof(buf);  
  
    n = strlen(token);  
    DeviceToken2Binary(token, n, binary, TOKEN_SIZE);  
  
    buflen = build_output_packet(buf, buflen, (const char*)binary, msg, badage, sound);  
    if (buflen <= 0)
{  
        return -1;  
    }  
  
    return SSL_write(ssl, buf, buflen);  
}  
  
int build_output_packet_2(char* buf, int buflen, /* 缓冲区及长度 */  
        uint32_t messageid, /* 消息编号 */  
        uint32_t expiry, /* 过期时间 */  
        const char* tokenbinary, /* 二进制Token */  
        char* msg, /* message */  
        int badage, /* badage */  
        const char * sound) /* sound */  
{  
    assert(buflen >= 1 + 4 + 4 + 2 + TOKEN_SIZE + 2 + MAX_PAYLOAD_SIZE);  
  
    char * pdata = buf;  
    // command  
    *pdata = 1;  
  
    // messageid  
    pdata++;  
    *(uint32_t*)pdata = messageid;  
  
    // expiry time  
    pdata += 4;  
    *(uint32_t*)pdata = htonl(expiry);  
  
    // token length  
    pdata += 4;  
    *(uint16_t*)pdata = htons(TOKEN_SIZE);  
  
    // token binary  
    pdata += 2;  
    memcpy(pdata, tokenbinary, TOKEN_SIZE);  
  
    pdata += TOKEN_SIZE;  
  
    int payloadlen = MAX_PAYLOAD_SIZE;  
    if (build_payload(pdata + 2, &payloadlen, msg, badage, sound) < 0)   
    {  
        msg[strlen(msg) - (payloadlen - MAX_PAYLOAD_SIZE)] = ‘\0‘;  
        payloadlen = MAX_PAYLOAD_SIZE;  
        if (build_payload(pdata + 2, &payloadlen, msg, badage, sound) <= 0)   
        {  
            return -1;  
        }  
    }  
  
    *(uint16_t*)pdata = htons(payloadlen);  
  
    return 1 + 4 + 4 + 2 + TOKEN_SIZE + 2 + payloadlen;  
}  
  
int send_message_2(SSL *ssl, const char* token, uint32_t id, uint32_t expire, char* msg, int badage, const char* sound)  
{  
    int         i, n;  
    char buf[1 + 4 + 4 + 2 + TOKEN_SIZE + 2 + MAX_PAYLOAD_SIZE];  
    unsigned char   binary[TOKEN_SIZE];  
    int buflen = sizeof(buf);  
  
    n = strlen(token);  
    printf("token length : %d, TOKEN_SIZE = %d\n token = %s\n", n, TOKEN_SIZE, token);  
    DeviceToken2Binary(token, n, binary, TOKEN_SIZE);  
  
    for (i = 0; i < TOKEN_SIZE; i++)
{
        printf("%d ", binary[i]); 
}
    printf("\n");  
  
  
    buflen = build_output_packet_2(buf, buflen, id, expire,(const char*)binary, msg, badage, sound);   
    if (buflen <= 0) 
{  
        return -1;  
    }
printf("buf = ");
for(i = 0; i<buflen;i++ )
{
  printf(" %02X",buf[i]);
}
printf("\n");
    n = SSL_write(ssl, buf, buflen);  
  
    return  n;  
}  
  
int main(int argc, char** argv)  
{  
    int  i, n;  
    char buf[1024];  
    char * msg = NULL;  
    if (argc > 1)
{
msg = argv[1];  
}
  
    init_openssl();  
  
    // 初始化Context  
    // develop.pem是我们的证书和Key,为了方便使用,我们把证书和Key写到同一个文件中  
    // 并取水了Key的密码保护  
    // entrust_2048_ca.pem是苹果证书的CA,它并不在Openssl的根证书中,所以需要我们手动指定,不然会无法验证  
    // 详细:http://www.entrust.net/developer/index.cfm  
    SSL_CTX *ctx = init_ssl_context("apns-pub-cert.pem", "apns-pub-key.pem", NULL, "entrust_2048_ca.pem");  
    if (!ctx)
{  
        fprintf(stderr, "init ssl context failed: %s\n", ERR_reason_error_string(ERR_get_error()));  
        return -1;  
    }  
  
    // 连接到测试服务器  
    const char* host = "gateway.push.apple.com";  
    const int port = 2195;  
    int socket = tcp_connect(host, port);  
    if (socket < 0) 
{  
        fprintf(stderr, "failed to connect to host %s\n", strerror(errno));  
        return -1;  
    }  
  
    // SSL连接  
    SSL *ssl = ssl_connect(ctx, socket);  
    if (!ssl) 
{  
fprintf(stderr, "ssl connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));  
close(socket);  
return -1;  
    }  
  
    // 验证服务器证书  
    if (verify_connection(ssl, host) != 0)
{  
fprintf(stderr, "verify failed\n");  
close(socket);  
return 1;  
    }  
  
    uint32_t msgid = 0;  
    uint32_t expire = time(NULL) + 24 * 3600; // expire 1 day  
  
    printf("main, expire = %d\n", expire);  
  
    if (!msg) 
{  
        msg = "hello 11111111111";  
    }  
  
    // 发送一条消息  
    const char* token = "803565d52a541078043edce4bbe715daefbd07c065d8fcb4de4afe660a3f8976";  
    n = send_message_2(ssl, token, msgid++, expire, msg, 1, "default");  
    printf("after send_message_2, n = %d\n", n);  
    if (n <= 0)   
    {  
        fprintf(stderr, "send failed: %s\n", ERR_reason_error_string(ERR_get_error()));  
    }
else  
    {  
        printf("send sucessfully.\n");  
    }  
  
  
    // 接收苹果推送服务器发来的数据:  
    //n = recv(socket, buf, sizeof(buf), MSG_DONTWAIT); //非阻塞模式接收    
    n = read(socket, buf, sizeof(buf)); //阻塞模式接收  
    printf("from APNS, n = %d\n", n);  
    for(i=0; i<n; i++)
{
        printf("%d ", buf[i]); 
}
    printf("\n");  
  
  
// 关闭连接  
SSL_shutdown(ssl);  
close(socket);  


printf("exit\n");  
  
    return 0;  
}  

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。