/* * acq_extsw.c - 외부 카드망 시뮬레이터 (밴사/VAN/발급망 대역). * 실제 카드 매입시스템이 소켓 위로 ISO 8583 전문을 주고받는 상대 endpoint 를 * 재현한다. 프레이밍: 4-hex 길이접두 + ISO 8583 전문(hex). * 수신 0200 → 파싱 → 승인판정 → 0210 응답 조립(DE38 승인번호, DE39 응답코드). * 금액 임계/난수로 일부 거절(05)·타임아웃 시늉. 마이그레이션시 이 소켓·프레이밍· * 전문 상태처리가 Spring 으로 옮기기 가장 까다로운 지점. */ #include #include #include #include #include #include #include #include #include "acq_iso8583.h" static int read_n(int fd, char *buf, int n) { int got = 0; while (got < n) { int r = read(fd, buf + got, n - got); if (r <= 0) return -1; got += r; } return got; } /* 승인번호 6자리 생성(STAN 기반 결정적) */ static void gen_approval(const char *stan, char *out) { unsigned h = 5381; const char *p = stan; while (*p) h = ((h << 5) + h) + (unsigned char)*p++; snprintf(out, 7, "%06u", h % 1000000); } static void handle(int cfd) { char lenhex[5], wire[4096]; if (read_n(cfd, lenhex, 4) != 4) return; lenhex[4] = 0; int len = (int)strtol(lenhex, NULL, 16); if (len <= 0 || len >= (int)sizeof(wire)) return; if (read_n(cfd, wire, len) != len) return; wire[len] = 0; iso_msg_t req; int c = iso_parse(wire, &req); if (c < 0) return; /* 승인판정: 금액 파싱, 임계 초과·STAN 말미로 일부 거절 */ long amt = atol(iso_get(&req, 4)); const char *stan = iso_get(&req, 11); int decline = (amt > 490000000) || ((stan[strlen(stan) > 0 ? strlen(stan) - 1 : 0] == '7') && amt > 3000000); iso_msg_t rsp; iso_init(&rsp); /* 응답 MTI = 요청 MTI + 0x10 (0200->0210, 0800->0810, 0420->0430) */ strncpy(rsp.mti, req.mti, 4); rsp.mti[3] = (req.mti[3] == '0') ? '0' : req.mti[3]; if (req.mti[2] == '0') rsp.mti[2] = '1'; /* 20x0 -> 21x0 */ else rsp.mti[2] = (req.mti[2] == '2') ? '3' : req.mti[2]; rsp.mti[4] = 0; /* 응답 필드: 요청 에코 + 응답코드/승인번호 */ iso_set(&rsp, 2, iso_get(&req, 2)); iso_set(&rsp, 3, iso_get(&req, 3)); iso_set(&rsp, 4, iso_get(&req, 4)); iso_set(&rsp, 7, iso_get(&req, 7)); iso_set(&rsp, 11, stan); iso_set(&rsp, 41, iso_get(&req, 41)); iso_set(&rsp, 42, iso_get(&req, 42)); iso_set(&rsp, 49, iso_get(&req, 49)); if (decline) { iso_set(&rsp, 39, "05"); /* 거절 */ } else { char appr[8]; gen_approval(stan, appr); iso_set(&rsp, 38, appr); iso_set(&rsp, 39, "00"); /* 승인 */ } /* RRN(DE37) = 전송일시+STAN 조합 */ char rrn[16]; snprintf(rrn, sizeof(rrn), "%.6s%.6s", iso_get(&req, 7), stan); iso_set(&rsp, 37, rrn); char out[4096]; int ol = iso_build(&rsp, out, sizeof(out)); if (ol < 0) return; char frame[4200]; int fl = snprintf(frame, sizeof(frame), "%04X%s", ol, out); write(cfd, frame, fl); } int main(int argc, char **argv) { int port = (argc > 1) ? atoi(argv[1]) : 9500; int sfd = socket(AF_INET, SOCK_STREAM, 0); int opt = 1; setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); return 1; } if (listen(sfd, 32) < 0) { perror("listen"); return 1; } fprintf(stderr, "acq_extsw: 외부 카드망 시뮬레이터 listening on :%d\n", port); for (;;) { int cfd = accept(sfd, NULL, NULL); if (cfd < 0) continue; handle(cfd); close(cfd); } return 0; }