IPP через HTTP: 400 неверных запросов в ответ
Я работаю на iOS и пытаюсь отправить HTTP-запрос POST для создания задания печати на принтере через Mac, который действует как сервер IPP. Я могу правильно печатать, используя airprint, я пытаюсь перейти на низкий уровень, потому что мне нужно отправить необработанные данные на принтер.
Что-то о моем коде:
Все жестко закодировано. Я все еще пытаюсь выяснить, где я ошибаюсь, поэтому я просто на стадии разработки / тестирования. Первая часть посвящена определению двоичной структуры запроса задания печати IPP (и оно должно быть правильным, согласно спецификации RFC 2910).
char data[239] = {0x01, 0x01, // IPP version
0x00, 0x04, // Print-job request
0x00, 0x00, 0x00, 0x50, // Arbitrary request ID
0x01, // Attribute group
// ATT 1
0x47, // charset value-tag
0x00, 0x12, // name-length
'a','t','t','r','i','b','u','t','e','s','-','c','h','a','r','s','e','t', // name
0x00, 0x05, // value-length
'u','t','f','-','8', // value
// ATT 2
0x48, // natural-language value-tag
0x00, 0x1B, // name-length
'a','t','t','r','i','b','u','t','e','s','-','n','a','t','u','r','a','l','-','l','a','n','g','u','a','g','e', // name
0x00, 0x05, // value-length
'e','n','-','u','s', // value
// ATT 3
0x45, // uri type value-tag
0x00, 0x0B, // name-length
'p','r','i','n','t','e','r','-','u','r','i', // name
0x00, 0x47, // value-length
'i','p','p',':','/','/','A','n','d','r','e','a','s','-','M','a','c','B','o','o','k','-','P','r','o','-','2','.','l','o','c','a','l','.',':','6','3','1','/','p','r','i','n','t','e','r','s','/','H','P','_','D','e','s','k','j','e','t','_','F','4','5','0','0','_','s','e','r','i','e','s', // value
// ATT 4
0x42, // requesting user id value-tag
0x00, 0x14, // name-length
'r','e','q','u','e','s','t','i','n','g','-','u','s','e','r','-','n','a','m','e', // name
0x00, 0x05, // value-length
'g','u','e','s','t', // value
// ATT 5
0x49, // document format value-tag
0x00, 0x0f, // document format
'd','o','c','u','m','e','n','t','-','f','o','r','m','a','t',
0x18, // TODO
'a','p','p','l','i','c','a','t','i','o','n','/','o','c','t','e','t','-','s','t','r','e','a','m',
0x03, // end of attributes
't','e','s','t'}; // data
NSMutableData *printJob = [NSMutableData data];
[printJob appendBytes:data length:sizeof(data)];
NSString* requestDataLengthString = [[NSString alloc] initWithFormat:@"%d", [printJob length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://192.168.1.23:631/printers/HP_Deskjet_F4500_series"]];
[request setHTTPMethod:@"POST"];
[request setValue:requestDataLengthString forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/ipp" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"Andreas-MacBook-Pro-2.local" forHTTPHeaderField:@"Host"];
[request setValue:@"CUPS/1.5.0" forHTTPHeaderField:@"User-Agent"];
[request setValue:@"100-continue" forHTTPHeaderField:@"Expect"];
[request setHTTPBody:printJob];
[[NSURLConnection connectionWithRequest:request delegate:self] start];
Всякий раз, когда я запускаю этот код, через 10 секунд я получаю неверный ответ HTTP 400.
Странно то, что HTTP-запрос выглядит точно так же, как и тот, который я могу записать с помощью анализатора пакетов, когда печать по воздуху отправляет задание на печать на ipp-сервер (и он работает).
2 ответа
Вы должны использовать правильный код операции ipp для Print-Job:
- неправильно:
0x00, 0x04, // =Validate-job request
- правильный:
0x00, 0x02, // Print-job request
Вы должны использовать формат документа text/plain
вместо octet-stream
, Также убедитесь, что принтер поддерживает формат данных или, по крайней мере, убедитесь, что CUPS настроен с соответствующими конвертерами (похоже, что вы разговариваете с принтером HP через CUPS).
Не беспокойся о user-agent
это не имеет значения. Вместо этого позаботьтесь о вашей обработке протокола http! Отправив Expect: 100-continue
Вы заставляете CUPS ожидать кусочек http. Не отправлять Expect-Header
и все должно быть хорошо.
Убедитесь, что длина ipp-запроса плюс длина данных правильно вычислена и помещена в заголовок content-length.
Следующие принты для меня. Мой принтер принимает IPP на порт 80, а также 631.
// See Print-Job Request sample on page 32
// in Appendix A: Protocol Examples
// of RFC 2910 IPP/1.1 Encoding and Transport
// https://tools.ietf.org/html/rfc2910
char data[239] = {0x01, 0x01, // IPP version
0x00, 0x02, // Print-job request
0x00, 0x00, 0x00, 0x50, // Arbitrary request ID
0x01, // Attribute group
// ATT 1
0x47, // charset value-tag
0x00, 0x12, // name-length
'a','t','t','r','i','b','u','t','e','s','-','c','h','a','r','s','e','t', // name
0x00, 0x05, // value-length
'u','t','f','-','8', // value
// ATT 2
0x48, // natural-language value-tag
0x00, 0x1B, // name-length
'a','t','t','r','i','b','u','t','e','s','-','n','a','t','u','r','a','l','-','l','a','n','g','u','a','g','e', // name
0x00, 0x02, // value-length
'e','n', // value
// ATT 3
0x45, // uri type value-tag
0x00, 0x0B, // name-length
'p','r','i','n','t','e','r','-','u','r','i', // name
0x00, 0x1A, // value-length
'i','p','p',':','/','/','1','7','2','.','0','2','0','.','0','1','0','.','0','0','8',':','0','8','0','/', // value
// ATT 4
0x42, // requesting user id value-tag
0x00, 0x14, // name-length
'r','e','q','u','e','s','t','i','n','g','-','u','s','e','r','-','n','a','m','e', // name
0x00, 0x09, // value-length
'a','s','s','o','c','i','a','t','e', // value
// ATT 5
0x49, // document format value-tag
0x00, 0x0f, // document format
'd','o','c','u','m','e','n','t','-','f','o','r','m','a','t',
0x00, 0x0f, // value-length
'a','p','p','l','i','c','a','t','i','o','n','/','p','d','f',
0x02, // start job-attributes tag
0x21, // integer type value-tag
0x00, 0x06, // name-length
'c','o','p','i','e','s',
0x00, 0x04, // value-length
0x00, 0x00, 0x00, 0x01, // 1 copy
// could add 'sides' with 'two-sided-long-edge' for double sided prints (see RFC)
0x03 // end of attributes
}; // follow with pdf file data
NSMutableData *printJob = [NSMutableData dataWithBytes:data length:sizeof(data)];
NSString *pdfFilePath = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"pdf"];
NSData* pdfData = [NSData dataWithContentsOfFile:pdfFilePath options:NSDataReadingUncached error:NULL];
[printJob appendData:pdfData];
NSString* requestDataLengthString = [[NSString alloc] initWithFormat:@"%ld", [printJob length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://172.020.010.008:080/"]];
[request setHTTPMethod:@"POST"];
[request setValue:requestDataLengthString forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/ipp" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:printJob];
NSError *error;
NSURLResponse *response;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
Что-то, что я предлагаю, если вам нужна помощь, чтобы увидеть рабочие примеры любого форматирования вызовов IPP, это использовать ipptool для генерации запроса и записи его в файл. Я не мог заставить прокси Burp перехватить его (даже с переменной окружения http_proxy), но этот скрипт узла добился цели:
const http = require('http');
const fs = require('fs');
http.createServer(function (req, res) {
console.log('Request Received');
var body = '';
res.writeHead(200, {
'Context-Type': 'text/plain',
'Access-Control-Allow-Origin': '*'
});
req.on('data', function (chunk) {
body += chunk;
});
req.on('end', function() {
fs.writeFileSync('file.txt', body, 'utf8');
res.end('{"msg": "OK"}');
})
}).listen(8080, '127.0.0.1');
console.log('Server running at http://127.0.0.1:8080/')
Затем вы можете позвонить на ваш сервер узла:
ipptool -vt -f sample.pdf ipp://127.0.0.1:8080/ print-job.test
и проверить запрос:
hexdump -C -n256 file.txt